/**
* @file tuya_device.c
* @author www.tuya.com
* @brief  demo for SDK WiFi & BLE for BK7231N, hardware: BK7231N NANO SoC board
* @version 0.1
* @date 2021-08-12
*
* @copyright Copyright (c) tuya.inc 2021
*
*/
#define _TUYA_DEVICE_GLOBAL
#include "uart_common.h"
//#include "gpio_test.h"
#include "gw_intf.h"
#include "tuya_device.h"
#include "tuya_gpio.h"
#include "tuya_iot_com_api.h"
#include "tuya_iot_internal_api.h"
#include "tuya_iot_wifi_api.h"
#include "tuya_key.h"
#include "tuya_led.h"
#include "tuya_os_adapter.h"
#include "tuya_uart.h"
#include "ty_cJSON.h"
#include "uni_log.h"
#include "uni_md5.h"
#include "uni_queue.h"
#include "uni_thread.h"
#include "uni_time.h"
#include "uni_time_queue.h"

/***********************************************************
*************************micro define***********************
***********************************************************/
#define DEBUG         0
#define WF_SW_VERSION USER_SW_VER
#define MUL_MAP_VER   0x00

#define KEY_TIMER_MS 20   /* key timer inteval */
#define KEY_RST_TIME 3000 /* Press reset time MS */

#define HN_RECV_BUF_MAX  (1024 + 32) /* Upper receive buffer */
#define HN_RECV_MIN_NUM  7           /* Minimum length of frame data */
#define JUMP_FIRST_TIME  3           /* Interval for sending the first packet heartbeat timer */
#define JUMP_SEND_TIME   15          /* Heartbeat timer sending interval */
#define JUMP_RECV_TIME   3           /* Heartbeat timer receiving times out */
#define WF_GUARD_TIME    90          /* Monitor the timing */
#define MCU_RECV_TIME    5           /* MCU upgrade receive timeout period */
#define TY_DP_TYPE       5
#define TY_DP_FRAME_MIN  5
#define QUEUE_MAX_EVENTS 10
#define SEND_UNIT        256
#define PROT_VER         0x00

#define WM_SUCCESS 0
#define WM_FAIL    1

#define NO_RET   0
#define NEED_RET 1

#define TP_NUMBER 0x00
#define TP_STRING 0x01

#define FAIL_CODE 0x00
#define SUCC_CODE 0x01

#define DP_RAW    0
#define DP_BOOL   1
#define DP_VALUE  2
#define DP_STRING 3
#define DP_ENUM   4
#define DP_BITMAP 5

#define WF_LIGHT_FAST_BLINK 0x00 /* Wifi light flash */
#define WF_LIGHT_SLOW_BLINK 0x01 /* Slow flash wifi lamp */
#define WF_LIGHT_OFF        0x02 /* Wifi light off */
#define WF_LIGHT_ON         0x03 /* wifi light on */
#define WF_MQ_ONLINE        0x04 /* mqtt connected */
#define WF_LOW_PWR          0x05 /* low power mode*/
#define WF_EZ_AP_MODE       0x06 /* WIFI  smartconfig&AP config status */

#define QRY_SEND_TIMR_OUT_MS 1000
#define TY_UART              TY_UART0
#define DATA_UP_OK           1
#define DATA_UP_FAIL         0
#define FIRST_JUMP           0x00
#define MCU_WIFI_MODE        0x00
#define WIFI_MODE            0x01
#define MCU_WIFI_MOD_LEN     0
#define WIFI_MOD_LEN         2
#define WIFI_SYNC_TIMEOUT    5

#define MAX_AP_NAME_LEN 33
#define MAX_AP_PASS_LEN 65

#define TOKN_LEN      14
#define AP_TEST_TIMER 500 /*ms*/

#define MCU_GU_ERR 0
#define MCU_GU_OK  1

#define MCU_FRIST_UP 1000 /*1S*/
#define MCU_AGAIN_UP 5000 /*5S*/

#define GET_MAC_OK  0
#define GET_MAC_ERR 1

#define RESULT_CODE_0 0
#define RESULT_CODE_1 1
#define RESULT_CODE_2 2

typedef enum {
    WIFI_DATA_ASYNC_UP,
    WIFI_DATA_SYNC_UP,
} WIFI_UP_TYPE;

/* uart config wifi*/
typedef enum {
    UT_CFG_SUCC = 0x00,
    UT_CFG_NOT_READY = 0x01,
    UT_CFG_DATA_ERR = 0x02,
    UT_CFG_OTHER_DATA_ERR = 0x03
} UART_CFG_STA;

typedef enum {
    WIFI_CONNECT_AP_TEST_NO,
    WIFI_CONNECT_AP_TEST_ING,
    WIFI_CONNECT_AP_TEST_OVER
} WIFI_CONNECT_TEST_STA;

typedef enum {
    HEART_BEAT_CMD = 0x00,
    PRODUCT_INFO_CMD,
    WORK_MODE_CMD,
    WIFI_STATE_CMD,
    WIFI_RESET_CMD,
    WIFI_RESET_SEL_CMD,
    DATA_CTRL_CMD,
    DATA_RPT_CMD,
    DATA_QUERY_CMD = 0x08,
    UPDATE_START_CMD = 0x0a,
    UPDATE_TRANS_CMD = 0x0b,
    UT_TIME_CMD = 0x0c,
    LOCAL_TIME_CMD = 0x1c,
    WIFI_TEST_CMD = 0x0e,
    MEMORY_QUERY_CMD = 0x0f,

    DATA_RPT_SYN_CMD = 0x22,
    DATA_RPT_ACK_CMD = 0x23,
    GET_WF_SIGAL_CMD = 0x24,
    STOP_HEART_BEAT_CMD = 0x25,
 
    UART_CINFIG_NET_CMD = 0x2a,
    GET_WIFI_STATE_CMD = 0x2b,
    WIFI_CONNECT_AP_TEST_CMD = 0x2c,
    GET_WIFI_MAC_CMD = 0x2d,
  
  
} WF_CMD_E;


typedef enum {
    UINX_TIMER              = 0x00,                     //UNIX时间
    LOCA_TIMER              = 0x01,                     //本地时间
}TIMER_SEVER_E;



typedef struct {
    UINT_T image_cnt;
    UINT_T file_size;
    UINT_T start_addr;
    UINT_T send_len;
    UCHAR_T stat;
} DEV_UG_PROC_S;



typedef struct {
    BOOL_T init_flg;
    TIMER_ID reset_repet_timer;
    BOOL_T is_wait_reset_inform_recv;
    SEM_HANDLE reset_sem_handle;
} TIMER_RESET_CFG_S;

#define FIRMWARE_KEY_LEN_MAX 16
typedef struct {
    BOOL_T flow_open;
    TY_UART_BAUD_E uart_bod;
    CHAR_T firm_key[FIRMWARE_KEY_LEN_MAX + 1];
} UART_INIT_S;

/* The format of a subcommand is ACK */
typedef struct {
    UCHAR_T sons_cmd; /*subcommand*/
    UCHAR_T ret;      /*result of enforcement*/
    UCHAR_T data[0];  /*Data content*/
} TY_SONS_ACK_CMD_S;

#pragma pack(1)

typedef struct
{
    WORD_T len;      /*Data length*/
    UCHAR_T data[0]; /*Data content*/
} TY_SEND_DATA_S;

typedef struct
{
    WORD_T head;     /* Fixed to 0x55AA (big-endian format) */
    UCHAR_T version; /* Version, upgrade extension for use */
    UCHAR_T fr_type; /* Specific frame type (command word) */
    WORD_T len;      /* Data length (big-endian format) */
    UCHAR_T data[0]; /* Data length */
} TY_FRAME_S;
//子命令格式
typedef struct {
    UCHAR_T sons_cmd; /* subcommand */
    UCHAR_T data[0];  /* Data content */
} TY_SONS_CMD_S;

typedef struct
{
    UCHAR_T mode;      /* Working mode,0 serial port processing 1 Module IO */
    UCHAR_T state_pin; /* Wifi status indicator */
    UCHAR_T reset_pin; /* Reset wifi  pin */
} TY_WIFI_WORK_S;

typedef struct
{
    UCHAR_T dpid;
    UCHAR_T type;
    WORD_T len;
    UCHAR_T data[0];
} TY_DATAPOINT_DATA_S;

typedef struct
{
    UINT_T offset;
    UINT_T fw_size;
} TY_UP_FW_S;

#pragma pack()

//MCU信息
typedef struct {
    UCHAR_T prt_ver;                         /*Data frame version*/
    TY_WIFI_WORK_S wf_work;                  /* MCU work mode */
    GW_PERMIT_DEV_TP_T tp;                   /* mcu farmware type  The default is 9 */
    CHAR_T dev_sw_ver[SW_VER_LEN + 1];       /* version info */
    CHAR_T product_key[PRODUCT_KEY_LEN + 1]; /* product key */
    GW_WF_CFG_MTHD_SEL cfg_mode;             /* wifi work mode */
    GW_WF_START_MODE start_mode;             /* wifi initialization mode */

} MCU_INFO_S;

typedef struct {
    CHAR_T ap_ssid[MAX_AP_NAME_LEN];
    CHAR_T ap_passwd[MAX_AP_PASS_LEN];
} TY_AP_TEST_INFO_S;

typedef struct
{
    UCHAR_T proc_buf[HN_RECV_BUF_MAX];
    P_QUEUE_CLASS pQueue;
    THRD_HANDLE thread;
    MUTEX_HANDLE mutex;
    TIMER_ID jump_send_timer;
    TIMER_ID queue_send_timer;
    TIMER_ID com_send_timer;
    TIMER_ID up_fw_timer;
    TIMER_ID guard_timer;
    TIMER_ID ap_test_timer;
    UCHAR_T try_cnt;
    TY_WIFI_WORK_S wf_work; /* MCU work mode */
    BOOL_T init_flag;       /* Device initialization flag */
    BOOL_T online_flag;     /* Device online flag */
    BOOL_T resend_flag;     /* The synchronization command is not returned and needs to be resend */
    UCHAR_T ack_stat;       /* The firmware upgrade package returns a response flag */
    UCHAR_T recv_state;
    UCHAR_T prt_ver;
    INT_T rcount;
    TY_UP_FW_S up_st;
    BOOL_T IsInUpgrade;
    BOOL_T IsMcuUgReset;
    BOOL_T StopHartBeat;
    SEM_HANDLE mcu_ug_sem;
    UCHAR_T timer_out_cnt;
    WIFI_CONNECT_TEST_STA cont_ap_sta;

    TY_AP_TEST_INFO_S ap_test_info; /* Wifi function test information */
} TY_MSG_S;

typedef struct {
    COUNTRY_CODE_E g_ccode; /* Tuya country code */
    UCHAR_T *s_code;        /* the country code string supported */
} FRAME_COUNTRY_CODE_CFG;

enum recv_proc {
    UART_RECV = 0,
    UART_PROC,
};

enum up_stat {
    ST_NO_SUPPORT = 0,
    ST_NOT_READY,
    ST_QUERY_FAIL,
    ST_NO_UPGRADE,
    ST_WAIT_UPGRADE,
};

enum fw_ack_stat {
    FW_IDE = 0,      /* Idle, packet sent, not returned */
    FW_START_RETURN, /* The start response packet is received */
    FW_TRANS_RETURN, /* The data response packet was received */
    FW_TIME_OUT,     /* Receiving response timeout */
    FW_FAIL,         /* fail */
};

enum media_flow_resp {
    FLOW_SUCC_E = 0x00,             /* success */
    FLOW_SERVER_NOT_READY_E = 0x01, /* The stream service is not enabled */
    FLOW_STAT_FALSE_E = 0x02,       /* The stream service is not connected */
    FLOW_DATA_TMOUT_E = 0x03,       /* Data push timeout */
    FLOW_DATA_LEN_ERR_E = 0x04,     /* The length of the transmitted data is incorrect */
};

enum wifi_test_result {
    WF_SCAN_FAIL = 0x00,
    WF_NO_AUTH = 0x01,
};

/*mcu ota per pack size*/
enum pack_size_unit {
    SIZE_256B = 0,
    SIZE_512B,
    SIZE_1024B,
};

typedef VOID (*DEV_RESET_UG_CB)(IN CONST GW_RESET_TYPE_E type);

/***********************************************************
*************************variable define********************
***********************************************************/
STATIC TY_MSG_S ty_msg;
STATIC UINT_T mcu_unit_len = SEND_UNIT;
STATIC LED_HANDLE wifi_light = NULL;
STATIC TM_MSG_S *g_sync_dev_ver;
STATIC DEV_UG_PROC_S dev_proc;
STATIC UART_INIT_S uart_init_str;

STATIC MCU_INFO_S mcu_info = {0};

/***********************************************************
*************************micro define***********************
***********************************************************/
STATIC OPERATE_RET device_differ_init(VOID);
STATIC VOID ty_datapoint_proc(ty_cJSON *root);
STATIC INT_T upload_wifi_state(VOID);
STATIC VOID start_guard_timer(UCHAR_T sec);
STATIC VOID del_queue_buf_by_cmd(UCHAR_T cmd);
STATIC VOID dev_ug_notify_cb(IN CONST FW_UG_S *fw, IN CONST INT_T download_result, IN PVOID_T pri_data);
STATIC VOID get_memory_cb(UINT_T timerID, PVOID_T pTimerArg);
STATIC VOID mcu_ota_sver_sync_cb(struct s_tm_msg *tm_msg);
STATIC OPERATE_RET dev_ug_process_cb(IN CONST FW_UG_S *fw, IN CONST UINT_T total_len, IN CONST UINT_T offset,
                                     IN CONST BYTE_T *data, IN CONST UINT_T len, OUT UINT_T *remain_len, IN PVOID_T pri_data);
//STATIC VOID set_rest_cb(DEV_RESET_UG_CB funcb);

STATIC TM_MSG_ENTRY_S tim_msg_tb[] = {
    {&(g_sync_dev_ver), mcu_ota_sver_sync_cb},
};

/**
* @brief mcu_ota_sver_sync_cb
*
* @param[in] none
* @return none
*/
STATIC VOID mcu_ota_sver_sync_cb(struct s_tm_msg *tm_msg)
{

    OPERATE_RET op_ret = OPRT_OK;
    op_ret = http_gw_update_version_v41();
    if (op_ret != OPRT_OK) {
        PR_ERR("http_gw_update_version_v41 err:%d", op_ret);
        cmmod_start_tm_msg(tm_msg, MCU_AGAIN_UP, TIMER_ONCE); //5s
    } else {
        wd_gw_desc_if_write(&(get_gw_cntl()->gw_if));
    }
}

/**
* @brief sys_timer_msg_funtion_init
*
* @param[in] none
* @return none
*/
STATIC OPERATE_RET sys_timer_msg_funtion_init(VOID)
{

    OPERATE_RET op_ret = OPRT_OK;
    UINT_T i = 0;

    for (i = 0; i < CNTSOF(tim_msg_tb); i++) {
        op_ret = cmmod_cr_tm_msg_hand(tim_msg_tb[i].tmm_msg_cb, NULL, tim_msg_tb[i].tm_msg);
        if (OPRT_OK != op_ret) {
            PR_ERR("cmmod_cr_tm_msg_hand err");
            goto EXIT;
        }
    }
    return OPRT_OK;

EXIT:
    for (i = 0; i < CNTSOF(tim_msg_tb); i++) {
        cmmod_release_tm_msg(*(tim_msg_tb[i].tm_msg));
        *(tim_msg_tb[i].tm_msg) = NULL;
    }
    return op_ret;
}

/**
* @brief set_unit_len
*
* @param[in] none
* @return none
*/
STATIC VOID set_unit_len(UINT_T len)
{
    mcu_unit_len = len;
}

/**
* @brief get_unit_len
*
* @param[in] none
* @return none
*/
STATIC UINT_T get_unit_len(VOID)
{
    return mcu_unit_len;
}

/**
* @brief getCheckSum
*
* @return none
*/
STATIC UCHAR_T getCheckSum(CONST UCHAR_T *pack, INT_T pack_len)
{
    UINT_T check_sum = 0;
    while (--pack_len >= 0) {
        check_sum += *pack++;
    }
    return check_sum & 0xff;
}

/**
* @brief close_heartbeat
*
* @return none
*/
STATIC VOID close_heartbeat(VOID)
{
    if ((TRUE == ty_msg.init_flag) && (ty_msg.StopHartBeat == FALSE)) {
        ty_msg.StopHartBeat = TRUE;
    }
}

/**
* @brief open_heartbeat
*
* @return none
*/
STATIC VOID open_heartbeat(VOID)
{
    ty_msg.StopHartBeat = FALSE;
}

/**
* @brief com_stop_timer
*
* @return none
*/
STATIC VOID com_stop_timer(VOID)
{
    if (0 == GetCurQueNum(ty_msg.pQueue)) {
        return;
    }

    ty_msg.try_cnt = 0;

    TY_SEND_DATA_S *pSend = NULL;
    GetQueueMember(ty_msg.pQueue, 1, (UCHAR_T *)&pSend, 1);
    if (pSend != NULL) {
        PR_DEBUG("free buffer... ...");
        Free(pSend), pSend = NULL;
    }
    DelQueueMember(ty_msg.pQueue, 1);
    sys_stop_timer(ty_msg.com_send_timer);
}

/**
* @brief com_stop_timer_cmd
*
* @return none
*/
STATIC VOID com_stop_timer_cmd(UCHAR_T cmd)
{
    del_queue_buf_by_cmd(cmd);
    sys_stop_timer(ty_msg.com_send_timer);
}

/**
* @brief ty_uart_send
*
* @return  WM_SUCCESS 0/ WM_FAIL 1
*/
STATIC INT_T ty_uart_send(UCHAR_T fr_type, WORD_T len, UCHAR_T *data)
{
    WORD_T head = 0x55aa;
    WORD_T send_len = SIZEOF(TY_FRAME_S) + len + 1;

    UCHAR_T *SendBuf = (UCHAR_T *)Malloc(send_len);
    if (SendBuf == NULL) {
        PR_ERR("ty_uart_send SendBuf is NULL");
        return WM_FAIL;
    }

    memset(SendBuf, 0, send_len);
    TY_FRAME_S *hn_frame = (TY_FRAME_S *)(SendBuf);
    hn_frame->head = WORD_SWAP(head);
    hn_frame->version = PROT_VER;
    hn_frame->fr_type = fr_type;
    hn_frame->len = WORD_SWAP(len);
    if (data && len) {
        memcpy(hn_frame->data, data, len);
    }
    UCHAR_T num = getCheckSum(SendBuf, send_len - 1);
    SendBuf[send_len - 1] = num;

    tuya_hal_mutex_lock(ty_msg.mutex);
    ty_uart_send_data(TY_UART, SendBuf, send_len);

#ifdef UART_TX_DEBUG
    INT_T i = 0;
    for (i = 0; i < send_len; i++) {
        PR_DEBUG_RAW("%02X ", SendBuf[i]);
    }
    PR_DEBUG("\r\n");
#endif

    Free(SendBuf);
    SendBuf = NULL;
    tuya_hal_mutex_unlock(ty_msg.mutex);

    return WM_SUCCESS;
}

/**
* @brief ty_uart_send
*
* @return  WM_SUCCESS 0/ WM_FAIL 1
*/
STATIC INT_T ty_msg_send(UCHAR_T fr_type, WORD_T len, UCHAR_T *data)
{
    WORD_T head = 0x55aa;
    WORD_T hn_send_len = SIZEOF(TY_SEND_DATA_S) + SIZEOF(TY_FRAME_S) + len + 1;

    UCHAR_T *SendBuf = (UCHAR_T *)Malloc(hn_send_len);
    if (SendBuf == NULL) {
        PR_ERR("SendBuf is NULL");
        return WM_FAIL;
    }

    memset(SendBuf, 0, hn_send_len);
    TY_FRAME_S *hn_frame = (TY_FRAME_S *)(SendBuf + SIZEOF(TY_SEND_DATA_S));
    hn_frame->head = WORD_SWAP(head);
    hn_frame->version = PROT_VER;
    hn_frame->fr_type = fr_type;
    hn_frame->len = WORD_SWAP(len);
    if (data && len) {
        memcpy(hn_frame->data, data, len);
    }
    UCHAR_T num = getCheckSum(SendBuf + SIZEOF(TY_SEND_DATA_S), hn_send_len - SIZEOF(TY_SEND_DATA_S) - 1);
    SendBuf[hn_send_len - 1] = num;

    TY_SEND_DATA_S *pSend = (TY_SEND_DATA_S *)SendBuf;
    pSend->len = hn_send_len - SIZEOF(TY_SEND_DATA_S);

    if (InQueue(ty_msg.pQueue, (UCHAR_T *)&pSend, 1) == 0) {
        Free(SendBuf);
        PR_ERR("InQueue err");
        return WM_FAIL;
    }
    return WM_SUCCESS;
}

/**
* @brief del_queue_buf_by_cmd
*
* @return  none
*/
STATIC VOID del_queue_buf_by_cmd(UCHAR_T cmd)
{
    TY_SEND_DATA_S *pSend = NULL;
    GetQueueMember(ty_msg.pQueue, 1, (UCHAR_T *)(&pSend), 1);
    if (NULL != pSend) {
        TY_FRAME_S *pFrame = (TY_FRAME_S *)pSend->data;
        if (pFrame->fr_type != cmd) {
            return;
        }
        DelQueueMember(ty_msg.pQueue, 1);
        Free(pSend);
        pSend = NULL;
    }
}

/**
* @brief del_queue_buf_by_sons_cmd
*
* @return none
*/
STATIC VOID del_queue_buf_by_sons_cmd(UCHAR_T cmd, UCHAR_T sons_cmd)
{
    TY_SEND_DATA_S *pSend = NULL;

    GetQueueMember(ty_msg.pQueue, 1, (UCHAR_T *)(&pSend), 1);
    if (NULL != pSend) {
        TY_FRAME_S *pFrame = (TY_FRAME_S *)pSend->data;
        if (pFrame->fr_type != cmd) {
            return;
        }
        TY_SONS_CMD_S *pFrame_sons = (TY_SONS_CMD_S *)pFrame->data;
        if (pFrame_sons->sons_cmd != sons_cmd) {
            return;
        }
        DelQueueMember(ty_msg.pQueue, 1);
        Free(pSend);
        pSend = NULL;
    }
}

/**
* @brief send_jump_pack
*
* @return  none
*/
STATIC VOID send_jump_pack(VOID)
{
    PR_NOTICE("send jump_pack");
    ty_msg_send(HEART_BEAT_CMD, 0, NULL);
}

/**
* @brief query_prod_info
*
* @return  none
*/
STATIC VOID query_prod_info(VOID)
{
    ty_msg_send(PRODUCT_INFO_CMD, 0, NULL);
}

/**
* @brief query_work_mode
*
* @return  none
*/
STATIC VOID query_work_mode(VOID)
{
    ty_msg_send(WORK_MODE_CMD, 0, NULL);
}

/**
* @brief query_dp_state
*
* @return  WM_SUCCESS 0/ WM_FAIL 1
*/
STATIC VOID query_dp_state(VOID)
{
    ty_uart_send(DATA_QUERY_CMD, 0, NULL);
}

/**
* @brief recv_jump_pack
*
* @return  WM_SUCCESS 0/ WM_FAIL 1
*/
STATIC INT_T recv_jump_pack(TY_FRAME_S *p_frame)
{

    UCHAR_T start = 0x00;
    com_stop_timer_cmd(HEART_BEAT_CMD);
    start_guard_timer(WF_GUARD_TIME);
    start = p_frame->data[0];

    if (FALSE == ty_msg.init_flag) {
        PR_DEBUG("Device power on ... ... version:%d", p_frame->version);
        ty_msg.prt_ver = p_frame->version;
        query_prod_info();
    } else {
        if (start == FIRST_JUMP) {
            if (ty_msg.wf_work.mode == MCU_WIFI_MODE) {
                upload_wifi_state();
            }
            query_dp_state();
        }
    }
    return 0;
}

/**
* @brief get_dev_info
*
* @return  OPRT_OK / ERROR
*/
STATIC OPERATE_RET get_dev_info(IN TY_FRAME_S *p_frame)
{
    if (NULL == p_frame) {
        PR_ERR("invalid param");
        return OPRT_INVALID_PARM;
    }

    WORD_T len = WORD_SWAP(p_frame->len);

    if (ty_msg.prt_ver < 0x03) {

        tuya_iot_wf_timeout_set(3 * 60);
        if ((len <= 0x0010) || (len > 0x018)) {
            PR_ERR("prod len = %d", len);
        }
        memset(mcu_info.product_key, 0, sizeof(mcu_info.product_key));
        memset(mcu_info.dev_sw_ver, 0, sizeof(mcu_info.dev_sw_ver));

        memcpy(mcu_info.product_key, p_frame->data, 16);

        if ((len - 16) > SW_VER_LEN) {
            memcpy(mcu_info.dev_sw_ver, p_frame->data + 16, SW_VER_LEN);
        } else {
            if ((len - 16) > 0) {
                memcpy(mcu_info.dev_sw_ver, p_frame->data + 16, len - 16);
            }
        }
    } else {

        UCHAR_T *p_data = (UCHAR_T *)Malloc(len + 1);
        if (NULL == p_data) {
            return OPRT_MALLOC_FAILED;
        }
        memset(p_data, 0, len + 1);
        memcpy(p_data, p_frame->data, len);

        PR_DEBUG("jstr:%s", p_data);
        ty_cJSON *root = ty_cJSON_Parse(p_data);
        if (NULL == root) {
            PR_ERR("cjson parse err");
            Free(p_data);
            p_data = NULL;
            return OPRT_CJSON_PARSE_ERR;
        }

        if ((NULL == ty_cJSON_GetObjectItem(root, "p")) ||
            (NULL == ty_cJSON_GetObjectItem(root, "v")) ||
            (NULL == ty_cJSON_GetObjectItem(root, "m"))) {
            PR_ERR("param is no correct");
            ty_cJSON_Delete(root);
            root = NULL;
            Free(p_data);
            p_data = NULL;
            return OPRT_INVALID_PARM;
        }

        ty_cJSON *json = NULL;
        json = ty_cJSON_GetObjectItem(root, "p");
        strncpy(mcu_info.product_key, json->valuestring, PRODUCT_KEY_LEN);

        json = ty_cJSON_GetObjectItem(root, "v");
        strncpy(mcu_info.dev_sw_ver, json->valuestring, SW_VER_LEN);

        json = ty_cJSON_GetObjectItem(root, "m");
        mcu_info.cfg_mode = json->valueint;

        json = ty_cJSON_GetObjectItem(root, "n");
        if (json != NULL) {
            if (json->valueint) {
                mcu_info.start_mode = WF_START_AP_ONLY;
            } else {
                mcu_info.start_mode = WF_START_SMART_AP_CONCURRENT;
            }
        } else {
            mcu_info.start_mode = WF_START_SMART_FIRST;
        }

        json = ty_cJSON_GetObjectItem(root, "mt");
        if (json != NULL) { //范围3-10分钟
            if ((json->valueint < 3) || (json->valueint > 10)) {
                PR_ERR("timeout params illegal");
                json->valueint = 3;
            }
            tuya_iot_wf_timeout_set((UINT_T)(json->valueint * 60));
        } else {
            tuya_iot_wf_timeout_set(3 * 60); //老的默认3分钟
        }

        json = ty_cJSON_GetObjectItem(root, "vt");
        if (json != NULL) {
            if ((json->valueint < 10) || (json->valueint > 19)) {
                PR_ERR("vt params illegal");
                cJSON_Delete(root);
                root = NULL;
                Free(p_data);
                p_data = NULL;
                return OPRT_INVALID_PARM;
            } else {
                mcu_info.tp = json->valueint;
            }
        } else {
            mcu_info.tp = 9;
        }

        ty_cJSON_Delete(root);
        root = NULL;
        Free(p_data);
        p_data = NULL;
    }
    return OPRT_OK;
}

/**
* @brief recv_prod_info
*
* @return  WM_SUCCESS 0/ WM_FAIL 1
*/
STATIC INT_T recv_prod_info(TY_FRAME_S *p_frame)
{

    PR_DEBUG("recv prod info");
    com_stop_timer_cmd(PRODUCT_INFO_CMD);
    OPERATE_RET op_ret = OPRT_OK;
    op_ret = get_dev_info(p_frame);
    if (OPRT_OK != op_ret) {
        return WM_FAIL;
    }
    query_work_mode();
    return WM_SUCCESS;
}

/**
* @brief recv_work_mode
*
* @return  OPRT_OK / ERROR
*/
STATIC OPERATE_RET recv_work_mode(TY_FRAME_S *p_frame)
{

    com_stop_timer_cmd(WORK_MODE_CMD);
    WORD_T len = WORD_SWAP(p_frame->len);
    OPERATE_RET op_ret = OPRT_OK;

    if (MCU_WIFI_MOD_LEN == len) {
        ty_msg.wf_work.mode = MCU_WIFI_MODE;
        PR_DEBUG("state mcu");
    } else if (WIFI_MOD_LEN == len) {
        ty_msg.wf_work.mode = WIFI_MODE;
        ty_msg.wf_work.state_pin = p_frame->data[0];
        ty_msg.wf_work.reset_pin = p_frame->data[1];
        PR_DEBUG("state pin:%d, reset pin:%d", ty_msg.wf_work.state_pin, ty_msg.wf_work.reset_pin);
    } else {
        PR_ERR("recv_work_mode len is err :%d", len);
    }

    if (TRUE == ty_msg.IsMcuUgReset) {

        ty_msg.IsMcuUgReset = FALSE;
        if (mcu_info.dev_sw_ver != NULL) {
            if (0 == is_valid_version((CHAR_T *)mcu_info.dev_sw_ver)) {
                PR_ERR("dev_sw_ver:%s", mcu_info.dev_sw_ver);
                return OPRT_INVALID_PARM;
            }

            GW_CNTL_S *gw_cntl = get_gw_cntl();
            gw_cntl->gw_if.attr_num = 1;
            PR_DEBUG("mcu_info.dev_sw_ver:%s,get_gw_cntl()->gw_if.dev_sw_ver:%s", mcu_info.dev_sw_ver, gw_cntl->gw_if.attr[0].ver);
            if (0 != strcasecmp(gw_cntl->gw_if.attr[0].ver, mcu_info.dev_sw_ver)) {
                memcpy(gw_cntl->gw_if.attr[0].ver, mcu_info.dev_sw_ver, SW_VER_LEN);
                gw_cntl->gw_if.attr[0].tp = mcu_info.tp;

                op_ret = cmmod_start_tm_msg(g_sync_dev_ver, MCU_FRIST_UP, TIMER_ONCE);
                if (OPRT_OK != op_ret) {
                    PR_ERR("stop_add_sub_init cmmod_start_tm_msg err:%d", op_ret);
                    return op_ret;
                }
            }
        }

    } else {
        if (FALSE == ty_msg.init_flag) {
            device_differ_init();
        }
    }
    ty_msg.init_flag = TRUE;
    return OPRT_OK;
}

/**
* @brief recv_wifi_ack
*
* @return  WM_SUCCESS 0/ WM_FAIL 1
*/
STATIC INT_T recv_wifi_ack(VOID)
{
    com_stop_timer_cmd(WIFI_STATE_CMD);
    return 0;
}

/**
* @brief upgrade_pre_cb
*
* @return  OPRT_OK/ERROR
*/
STATIC OPERATE_RET upgrade_pre_cb(IN CONST UINT_T file_size)
{
    PR_DEBUG("file_size = %d", file_size);

    UCHAR_T start_upgrade = UPDATE_START_CMD;
    UCHAR_T len_buf[4];
    UINT_T icnt = 0, len;

    if (file_size == 0) {
        PR_ERR("file_size = %d", file_size);
        return OPRT_COM_ERROR;
    }

    ty_msg.up_st.offset = 0;
    ty_msg.up_st.fw_size = file_size;
    ty_msg.ack_stat = FW_IDE;
    len = file_size;
    len_buf[0] = (len >> 24) & 0xFF;
    len_buf[1] = (len >> 16) & 0xFF;
    len_buf[2] = (len >> 8) & 0xFF;
    len_buf[3] = (len)&0xFF;
    ty_uart_send(start_upgrade, 4, len_buf);
    sys_start_timer(ty_msg.up_fw_timer, MCU_RECV_TIME * 1000, TIMER_ONCE);

    while (1) {

        tuya_hal_semaphore_wait(ty_msg.mcu_ug_sem);
        if (ty_msg.ack_stat == FW_TIME_OUT) {
            icnt++;
            if (icnt >= 3) {
                ty_msg.ack_stat = FW_FAIL;
                return OPRT_COM_ERROR;
            }
            PR_DEBUG("start upgrade again");
            sys_start_timer(ty_msg.up_fw_timer, MCU_RECV_TIME * 1000, TIMER_ONCE);
            ty_uart_send(start_upgrade, 4, len_buf); //超时再次启动
        } else if (ty_msg.ack_stat == FW_START_RETURN) {
            break;
        } else if (ty_msg.ack_stat == FW_IDE) {
            continue;
        } else {
            PR_ERR("ty_msg.ack_stat:%d", ty_msg.ack_stat);
            return OPRT_COM_ERROR;
        }
    }
    return OPRT_OK;
}

/**
* @brief upgrade_file_cb
*
* @return  OPRT_OK/ERROR
*/
STATIC OPERATE_RET upgrade_file_cb(IN CONST BYTE_T *data, IN CONST UINT_T len)
{
    UCHAR_T head_buf[10] = {0};
    UCHAR_T sum_check = 0;
    UCHAR_T icnt = 0;
    WORD_T data_len = 0;
    WORD_T head_len = 0;

    PR_DEBUG("upgrade len = %d,FreeHeapSize = %d", len, tuya_hal_system_getheapsize());

    if (ty_msg.ack_stat == FW_FAIL) {
        return OPRT_COM_ERROR;
    }

    if (ty_msg.prt_ver == 0x00) {
        data_len = len + 2;
    } else {
        data_len = len + 4;
    }

    head_buf[0] = 0x55;
    head_buf[1] = 0xaa;
    head_buf[2] = 0x00;
    head_buf[3] = 0x0b;
    head_buf[4] = (data_len >> 8) & 0xFF;
    head_buf[5] = (data_len & 0xFF);
    if (ty_msg.prt_ver == 0x00) {
        head_buf[6] = ((ty_msg.up_st.offset >> 8) & 0xFF);
        head_buf[7] = (ty_msg.up_st.offset & 0xFF);
        head_len = 8;
    } else {
        head_buf[6] = ((ty_msg.up_st.offset >> 24) & 0xFF);
        head_buf[7] = ((ty_msg.up_st.offset >> 16) & 0xFF);
        head_buf[8] = ((ty_msg.up_st.offset >> 8) & 0xFF);
        head_buf[9] = (ty_msg.up_st.offset & 0xFF);
        head_len = 10;
    }
    sum_check = getCheckSum(head_buf, head_len) + getCheckSum(data, len);

    tuya_hal_mutex_lock(ty_msg.mutex);
    ty_uart_send_data(TY_UART, head_buf, head_len);
    ty_uart_send_data(TY_UART, (UCHAR_T *)data, len);
    ty_uart_send_data(TY_UART, &sum_check, 1);
    tuya_hal_mutex_unlock(ty_msg.mutex);

    sys_start_timer(ty_msg.up_fw_timer, MCU_RECV_TIME * 1000, TIMER_ONCE);
    ty_msg.up_st.offset += len;

    while (1) {

        tuya_hal_semaphore_wait(ty_msg.mcu_ug_sem);
        if ((ty_msg.ack_stat == FW_TIME_OUT) || (ty_msg.ack_stat == FW_START_RETURN)) {
            icnt++;
            if (icnt >= 3) {

                PR_ERR("ty_msg.ack_stat:%d", ty_msg.ack_stat);
                ty_msg.ack_stat = FW_FAIL;
                return OPRT_COM_ERROR;
            }
            PR_DEBUG("try again");
            sys_start_timer(ty_msg.up_fw_timer, MCU_RECV_TIME * 1000, TIMER_ONCE);

            tuya_hal_mutex_lock(ty_msg.mutex);
            ty_uart_send_data(TY_UART, head_buf, head_len);
            ty_uart_send_data(TY_UART, (UCHAR_T *)data, len);
            ty_uart_send_data(TY_UART, &sum_check, 1);
            tuya_hal_mutex_unlock(ty_msg.mutex);
        } else if (ty_msg.ack_stat == FW_TRANS_RETURN) {

            break;
        } else {
            PR_ERR("ty_msg.ack_stat:%d", ty_msg.ack_stat);
            ty_msg.ack_stat = FW_FAIL;
            return OPRT_COM_ERROR;
        }
    }

    if (ty_msg.up_st.offset == ty_msg.up_st.fw_size) {

        if (ty_msg.prt_ver == 0x00) {
            head_buf[4] = 0x00;
            head_buf[5] = 0x02;
            head_buf[6] = ((ty_msg.up_st.offset >> 8) & 0xFF);
            head_buf[7] = (ty_msg.up_st.offset & 0xFF);
            head_len = 8;
        } else {
            head_buf[4] = 0x00;
            head_buf[5] = 0x04;
            head_buf[6] = ((ty_msg.up_st.offset >> 24) & 0xFF);
            head_buf[7] = ((ty_msg.up_st.offset >> 16) & 0xFF);
            head_buf[8] = ((ty_msg.up_st.offset >> 8) & 0xFF);
            head_buf[9] = (ty_msg.up_st.offset & 0xFF);
            head_len = 10;
        }
        sum_check = getCheckSum(head_buf, head_len);

        tuya_hal_mutex_lock(ty_msg.mutex);
        ty_uart_send_data(TY_UART, head_buf, head_len);
        ty_uart_send_data(TY_UART, &sum_check, 1);
        tuya_hal_mutex_unlock(ty_msg.mutex);
        PR_NOTICE("last pack");

        ty_msg.init_flag = FALSE;
        ty_msg.IsMcuUgReset = TRUE;
        ty_msg.IsInUpgrade = FALSE;
        ty_msg.StopHartBeat = FALSE;
        PR_DEBUG(" init_flag[%d] IsMcuUgReset[%d]  IsInUpgrade[%d]", ty_msg.init_flag, ty_msg.IsMcuUgReset, ty_msg.IsInUpgrade);
    }

    return OPRT_OK;
}

/**
* @brief long_to_byte
*
* @return  none
*/
STATIC VOID long_to_byte(UINT_T val, UCHAR_T *out)
{
    if (NULL == out) {
        PR_ERR("long_to_byte parm is null");
        return;
    }

    out[0] = (UCHAR_T)((val & 0xFF000000) >> 24);
    out[1] = (UCHAR_T)((val & 0x00FF0000) >> 16);
    out[2] = (UCHAR_T)((val & 0x0000FF00) >> 8);
    out[3] = (UCHAR_T)((val & 0x000000FF));

    return;
}








/**
* @brief recv_upgrade_start
*
* @return  WM_SUCCESS 0/ WM_FAIL 1
*/
STATIC INT_T recv_upgrade_start(TY_FRAME_S *p_frame)
{
    if (NULL == p_frame) {
        PR_ERR("recv_upgrade_start parm is null");
        return -1;
    }

    sys_stop_timer(ty_msg.up_fw_timer);
    ty_msg.prt_ver = p_frame->version;
    ty_msg.ack_stat = FW_START_RETURN;
    if (p_frame->len > 0) {
        switch (p_frame->data[0]) {
        case SIZE_256B:
            set_unit_len(256);
            break;
        case SIZE_512B:
            set_unit_len(512);
            break;
        case SIZE_1024B:
            set_unit_len(1024);
            break;
        default:
            set_unit_len(256);
            break;
        }
    }

    tuya_hal_semaphore_post(ty_msg.mcu_ug_sem);
    PR_DEBUG("recv upgrade start");
    return 0;
}

/**
* @brief recv_upgrade_transfer
*
* @return  WM_SUCCESS 0/ WM_FAIL 1
*/
STATIC INT_T recv_upgrade_transfer(VOID)
{
    sys_stop_timer(ty_msg.up_fw_timer);
    ty_msg.ack_stat = FW_TRANS_RETURN;
    PR_DEBUG("transfer ack\r\n");
    tuya_hal_semaphore_post(ty_msg.mcu_ug_sem);
    return 0;
}

/**
* @brief recv_sys_datetime
*
* @return  WM_SUCCESS 0/ WM_FAIL 1
*/
STATIC INT_T recv_sys_datetime(VOID)
{

    OPERATE_RET op_ret = OPRT_OK;

    POSIX_TM_S st_time;
    UCHAR_T pTime[7];

    memset(&st_time, 0, SIZEOF(POSIX_TM_S));
    memset(&pTime, 0, SIZEOF(pTime));

    op_ret = uni_time_check_time_sync();
    if (op_ret != OPRT_OK) {
        pTime[0] = FAIL_CODE;
        goto EXIT;
    }

    PR_DEBUG("uni_time_check_time_sync:%d", op_ret);
    op_ret = uni_time_get(&st_time);
    if ((op_ret != OPRT_OK) || (st_time.tm_year <= 100)) {
        pTime[0] = FAIL_CODE;
        goto EXIT;
    }

    pTime[0] = SUCC_CODE;

    PR_DEBUG("year:%d mon:%d day:%d", st_time.tm_year, st_time.tm_mon, st_time.tm_mday);
    PR_DEBUG("hour:%d min:%d sec:%d", st_time.tm_hour, st_time.tm_min, st_time.tm_sec);

    pTime[1] = (st_time.tm_year - 100) & 0xFF; //year since 2000
    pTime[2] = (st_time.tm_mon + 1) & 0xFF;    //mon 1 - 12
    pTime[3] = (st_time.tm_mday) & 0xFF;
    pTime[4] = (st_time.tm_hour) & 0xFF;
    pTime[5] = (st_time.tm_min) & 0xFF;
    pTime[6] = (st_time.tm_sec) & 0xFF;
    ty_uart_send(UT_TIME_CMD, 7, pTime);

    return OPRT_OK;

EXIT:
    ty_uart_send(UT_TIME_CMD, 7, pTime);
    return OPRT_OK;
}

/**
* @brief recv_local_datetime
*
* @return  WM_SUCCESS 0/ WM_FAIL 1
*/
STATIC INT_T recv_local_datetime(VOID)
{
    UCHAR_T fr_type = LOCAL_TIME_CMD;
    OPERATE_RET op_ret = OPRT_OK;

    UCHAR_T *pTime = (UCHAR_T *)Malloc(8 + 1);
    if (pTime == NULL) {
        PR_ERR("malloc err");
        return WM_FAIL;
    }
    memset(pTime, 0, 8 + 1);

    POSIX_TM_S st_time;
    memset(&st_time, 0, SIZEOF(POSIX_TM_S));

    op_ret = uni_time_check_time_sync();
    if (op_ret != OPRT_OK) {
        pTime[0] = FAIL_CODE;
        goto EXIT;
    }

    op_ret = uni_local_time_get(&st_time);
    if (OPRT_OK != op_ret) {
        PR_ERR("get_local_time op_ret:%d", op_ret);
        goto EXIT;
    }

    pTime[0] = SUCC_CODE;
    pTime[1] = (st_time.tm_year - 100) & 0xFF;
    pTime[2] = (st_time.tm_mon + 1) & 0xFF;
    pTime[3] = (st_time.tm_mday) & 0xFF;
    pTime[4] = (st_time.tm_hour) & 0xFF;
    pTime[5] = (st_time.tm_min) & 0xFF;
    pTime[6] = (st_time.tm_sec) & 0xFF;
    pTime[7] = (st_time.tm_wday) & 0xFF;
    if (pTime[7] == 0) {
        pTime[7] = 7;
    }

EXIT:
    ty_uart_send(fr_type, 8, pTime);
    Free(pTime);
    pTime = NULL;
    return WM_SUCCESS;
}

/**
* @brief recv_uart_wf_net
*
* @return  WM_SUCCESS 0/ WM_FAIL 1
*/
STATIC INT_T recv_uart_wf_net(UCHAR_T fr_type, UINT_T fr_len, UCHAR_T *data)
{

    BYTE_T ret = UT_CFG_SUCC;

    WORD_T len = WORD_SWAP(fr_len);

    if (!ty_msg.init_flag) {
        PR_ERR("wf init_flag false:%d ", ty_msg.init_flag);
        ret = UT_CFG_NOT_READY;
        goto ERR_EXIT;
    }

    GW_WIFI_NW_STAT_E cur_nw_stat;
    get_wf_gw_nw_status(&cur_nw_stat);
    if ((cur_nw_stat != STAT_UNPROVISION) && (cur_nw_stat != STAT_AP_STA_UNCFG) && (cur_nw_stat != STAT_UNPROVISION_AP_STA_UNCFG)) {
        PR_ERR("wifi net station no right :%d", cur_nw_stat);
        ret = UT_CFG_NOT_READY;
        goto ERR_EXIT;
    }

    CHAR_T *tmp = Malloc(len + 1);
    if (tmp == NULL) {
        ret = UT_CFG_OTHER_DATA_ERR;
        PR_ERR("recv_uart_wf_net Malloc err");
        goto ERR_EXIT;
    }

    memset(tmp, 0, len + 1);
    memcpy(tmp, data, len);

    ty_cJSON *root = NULL;
    root = ty_cJSON_Parse(tmp);
    if (NULL == root) {
        PR_ERR("parse error");
        ret = 0;
        Free(tmp);
        tmp = NULL;
        ret = UT_CFG_DATA_ERR;
        goto ERR_EXIT;
    }
    Free(tmp);
    tmp = NULL;

    if ((NULL == ty_cJSON_GetObjectItem(root, "s")) ||
        (NULL == ty_cJSON_GetObjectItem(root, "t")) ||
        (NULL == ty_cJSON_GetObjectItem(root, "p"))) {
        PR_ERR("param is no correct");
        ty_cJSON_Delete(root);
        ret = UT_CFG_DATA_ERR;
        goto ERR_EXIT;
    }

    if (ty_cJSON_GetObjectItem(root, "s")->valuestring == NULL ||
        ty_cJSON_GetObjectItem(root, "t")->valuestring == NULL) {
        ty_cJSON_Delete(root);
        PR_ERR("data error");
        ret = UT_CFG_DATA_ERR;
        goto ERR_EXIT;
    }

    if (strlen(ty_cJSON_GetObjectItem(root, "t")->valuestring) != TOKN_LEN) {
        PR_ERR("token invalid");
        ty_cJSON_Delete(root);
        ret = UT_CFG_DATA_ERR;
        goto ERR_EXIT;
    }

    OPERATE_RET op_ret = OPRT_OK;
    op_ret = tuya_iot_gw_wf_user_cfg(ty_cJSON_GetObjectItem(root, "s")->valuestring,
                                     ty_cJSON_GetObjectItem(root, "p")->valuestring,
                                     ty_cJSON_GetObjectItem(root, "t")->valuestring);
    if (op_ret != OPRT_OK) {
        PR_ERR("op_ret:%d", op_ret);
        ret = UT_CFG_DATA_ERR;
        ty_cJSON_Delete(root);
        goto ERR_EXIT;
    }

    ty_cJSON_Delete(root);
    ret = UT_CFG_SUCC;
    ty_uart_send(fr_type, SIZEOF(BYTE_T), &ret);
    return OPRT_OK;

ERR_EXIT:
    ty_uart_send(fr_type, SIZEOF(BYTE_T), &ret);
    return OPRT_COM_ERROR;
}

/**
* @brief wf_start_connect_ap
*
* @return  OPRT_OK/ERROR
*/
STATIC OPERATE_RET wf_start_connect_ap(IN CONST CHAR_T *ssid, IN CONST CHAR_T *passwd, IN CONST GW_WIFI_NW_STAT_E wf_sta)
{
    OPERATE_RET op_ret = OPRT_OK;

    if (STAT_UNPROVISION == wf_sta) {
        PR_DEBUG("wifi out ez mode into connect test mode");
        wf_nw_cfg_stop();
    }

    op_ret = wf_wk_mode_set(WWM_STATION);
    if (OPRT_OK != op_ret) {
        PR_ERR("wf_wk_mode_set error:%d", op_ret);
        return op_ret;
    }

    op_ret = wf_station_disconnect();
    if (op_ret != OPRT_OK) {
        PR_ERR("wf_station_disconnect err:%d", op_ret);
        return op_ret;
    }

    return OPRT_OK;
}

/**
* @brief recv_upgrade_start
*
* @return  WM_SUCCESS 0/ WM_FAIL 1
*/
STATIC VOID recv_func_ap_connect_test(WORD_T len, UCHAR_T *data)
{

    WORD_T data_len = WORD_SWAP(len);
    UCHAR_T result = FAIL_CODE;

    if ((0 == data_len) || (NULL == data)) {
        PR_ERR("recv_func_ap_connect_test parm is err");
        goto EXIT;
    }

    if (!ty_msg.init_flag) {
        PR_ERR("wf init_flag false:%d ", ty_msg.init_flag);
        goto EXIT;
    }

    if (WIFI_CONNECT_AP_TEST_ING == ty_msg.cont_ap_sta) {
        PR_NOTICE("wifi connect ap test is ing");
        return;
    }

    GW_WIFI_NW_STAT_E cur_nw_stat;
    get_wf_gw_nw_status(&cur_nw_stat);
    if (((cur_nw_stat > STAT_AP_STA_UNCFG) && (cur_nw_stat < STAT_UNPROVISION_AP_STA_UNCFG)) && (WIFI_CONNECT_AP_TEST_NO == ty_msg.cont_ap_sta)) {
        PR_ERR("wifi net station no right :%d", cur_nw_stat);
        goto EXIT;
    }

    CHAR_T *data_buf = Malloc(data_len + 1);
    if (NULL == data_buf) {
        PR_ERR("recv_func_ap_connect_test Malloc err");
        goto EXIT;
    }

    memset(data_buf, 0, data_len + 1);
    memcpy(data_buf, data, data_len);

    PR_NOTICE("ap_connect jstr:%s", data_buf);

    ty_cJSON *root = ty_cJSON_Parse(data_buf);
    Free(data_buf);
    data_buf = NULL;

    if (NULL == root) {
        PR_ERR("cjson parse err");
        goto EXIT;
    }

    if ((NULL == ty_cJSON_GetObjectItem(root, "ssid")) ||
        (NULL == ty_cJSON_GetObjectItem(root, "password"))) {
        PR_ERR("param is no correct");
        ty_cJSON_Delete(root);
        root = NULL;
        goto EXIT;
    }

    CHAR_T *ap_ssid = NULL;
    CHAR_T *ap_password = NULL;
    ap_ssid = (CHAR_T *)Malloc(MAX_AP_NAME_LEN);
    if (NULL == ap_ssid) {
        PR_ERR("Malloc ap_ssid is fail");
        ty_cJSON_Delete(root);
        root = NULL;
        goto EXIT;
    }
    memset(ap_ssid, 0, MAX_AP_NAME_LEN);

    ap_password = (UCHAR_T *)Malloc(MAX_AP_PASS_LEN);
    if (NULL == ap_password) {
        PR_ERR("Malloc ap_ssid is fail");
        Free(ap_ssid);
        ap_ssid = NULL;
        ty_cJSON_Delete(root);
        root = NULL;
        goto EXIT;
    }
    memset(ap_password, 0, MAX_AP_PASS_LEN);

    ty_cJSON *json = NULL;
    json = ty_cJSON_GetObjectItem(root, "ssid");
    strncpy(ap_ssid, json->valuestring, MAX_AP_NAME_LEN);

    json = ty_cJSON_GetObjectItem(root, "password");
    strncpy(ap_password, json->valuestring, MAX_AP_PASS_LEN);

    ty_cJSON_Delete(root);
    root = NULL;

    result = SUCC_CODE;
    ty_msg.cont_ap_sta = WIFI_CONNECT_AP_TEST_ING;
    ty_uart_send(WIFI_CONNECT_AP_TEST_CMD, 1, &result);
    memset(ty_msg.ap_test_info.ap_ssid, 0, MAX_AP_NAME_LEN);
    memset(ty_msg.ap_test_info.ap_passwd, 0, MAX_AP_PASS_LEN);
    memcpy(ty_msg.ap_test_info.ap_ssid, ap_ssid, MAX_AP_NAME_LEN);
    memcpy(ty_msg.ap_test_info.ap_passwd, ap_password, MAX_AP_PASS_LEN);

    OPERATE_RET op_ret = OPRT_OK;
    op_ret = wf_start_connect_ap(ap_ssid, ap_password, cur_nw_stat);
    Free(ap_ssid);
    ap_ssid = NULL;
    Free(ap_password);
    ap_password = NULL;
    if (op_ret != OPRT_OK) {
        PR_ERR("start_connect_ap err:%d", op_ret);
        ty_msg.cont_ap_sta = WIFI_CONNECT_AP_TEST_NO;
        goto EXIT;
    }

    if (FALSE == IsThisSysTimerRun(ty_msg.ap_test_timer)) {
        sys_start_timer(ty_msg.ap_test_timer, AP_TEST_TIMER, TIMER_CYCLE);
    }

    PR_DEBUG("recv_func_ap_connect_test is ok");
    return;

EXIT:
    result = FAIL_CODE;
    ty_uart_send(WIFI_CONNECT_AP_TEST_CMD, 1, &result);
    return;
}

/**
* @brief recv_func_test
*
* @return  none
*/
STATIC VOID recv_func_test(VOID)
{

    UCHAR_T fr_type = WIFI_TEST_CMD;
    OPERATE_RET op_ret = OPRT_OK;
    UCHAR_T buf[2];

    GW_BASE_IF_S gw_data;
    memset(&gw_data, 0, SIZEOF(GW_BASE_IF_S));
    wd_gw_base_if_read(&gw_data);
#if TY_SECURITY_CHIP
    if (!gw_data.has_auth) {
#else
    if (0 == gw_data.auth_key[0] ||
        0 == gw_data.uuid[0]) {
#endif
        PR_NOTICE("please write uuid and auth_key first");
        buf[0] = 0x00;
        buf[1] = WF_NO_AUTH;
        ty_uart_send(fr_type, 2, buf);
        return;
    }

    AP_IF_S *ap = NULL;
    op_ret = wf_assign_ap_scan("tuya_mdev_test", &ap); //lql
    if (OPRT_OK != op_ret) {
        buf[0] = 0x00;
        buf[1] = WF_SCAN_FAIL;
    } else {
        SCHAR_T result = ap->rssi;
        buf[0] = 0x01;
        if (result <= -100) {
            buf[1] = 0;
        } else if ((result > -100) && (result <= -80)) {
            buf[1] = 40;
        } else if ((result > -80) && (result <= -60)) {
            buf[1] = 60;
        } else if ((result > -60) && (result <= -40)) {
            buf[1] = 80;
        } else {
            buf[1] = 100;
        }
        PR_NOTICE("scan ap rssi :%d", result);
    }

    wf_release_ap(ap);
    ty_uart_send(fr_type, 2, buf);
    return;
}

/**
* @brief recv_mem_check
*
* @return  none
*/

STATIC VOID recv_mem_check(VOID)
{
    UCHAR_T fr_type = MEMORY_QUERY_CMD;
    CHAR_T buf[5];

    memset(buf, 0, sizeof(buf));
    INT_T size = tuya_hal_system_getheapsize(); //lql

    memset(buf, 0, sizeof(buf));
    buf[0] = ((size >> 24) & 0xFF);
    buf[1] = ((size >> 16) & 0xFF);
    buf[2] = ((size >> 8) & 0xFF);
    buf[3] = (size & 0xFF);

    ty_uart_send(fr_type, 4, buf);
    return;
}

/**
* @brief send_wf_signal
*
* @return  none
*/
STATIC VOID send_wf_signal(VOID)
{
    SCHAR_T rssi = 0x00;
    GW_WIFI_NW_STAT_E cur_nw_stat;
    get_wf_gw_nw_status(&cur_nw_stat);
    if (((cur_nw_stat <= STAT_AP_STA_UNCFG) || (cur_nw_stat == STAT_UNPROVISION_AP_STA_UNCFG)) && (ty_msg.cont_ap_sta < WIFI_CONNECT_AP_TEST_OVER)) {
        PR_ERR("wifi net station no right :%d", cur_nw_stat);
        ty_uart_send(GET_WF_SIGAL_CMD, 1, &rssi);
        return;
    }

    OPERATE_RET op_ret = wf_station_get_conn_ap_rssi(&rssi);
    if (OPRT_OK != op_ret) {
        ty_uart_send(GET_WF_SIGAL_CMD, 1, (UCHAR_T *)&rssi);
        return;
    }

    PR_DEBUG("RSSI:%d", rssi);
    ty_uart_send(GET_WF_SIGAL_CMD, 1, (UCHAR_T *)&rssi);
    return;
}

/**
* @brief recv_stop_heart_beat
*
* @return  none
*/
STATIC VOID recv_stop_heart_beat(VOID)
{
    close_heartbeat();
    ty_uart_send(STOP_HEART_BEAT_CMD, 0, NULL);
    return;
}

/**
* @brief recv_stop_heart_beat
*
* @return  none
*/
STATIC INT_T reset_wifi_pro(VOID)
{
    ty_uart_send(WIFI_RESET_CMD, 0, NULL);
    tuya_iot_wf_gw_unactive();
    return 0;
}

/**
* @brief reset_wifi_select
*
* @return  WM_SUCCESS 0/ WM_FAIL 1
*/
STATIC INT_T reset_wifi_select(TY_FRAME_S *p_frame)
{
    WORD_T len = WORD_SWAP(p_frame->len);
    ty_uart_send(WIFI_RESET_SEL_CMD, 0, NULL);
    if (ty_msg.wf_work.mode == MCU_WIFI_MODE) {
        if (len > 0) {
            if (p_frame->data[0] == 0) {
                iot_wf_gw_unactive_custom_mode(WF_START_SMART_FIRST);
            } else {
                iot_wf_gw_unactive_custom_mode(WF_START_AP_FIRST);
            }
        } else {
            PR_ERR("cmd para err"); //lql
        }
    }
    return 0;
}

/**
* @brief get_wf_connect_ap_test_sta
*
* @return  OPRT_OK/ERROR
*/
STATIC OPERATE_RET get_wf_connect_ap_test_sta(OUT GW_WIFI_NW_STAT_E *nw_stat)
{

    if (NULL == nw_stat) {
        PR_ERR("get_wf_connect_ap_test_sta parm is NULL err");
        return OPRT_INVALID_PARM;
    }
    OPERATE_RET op_ret = OPRT_OK;

    WF_STATION_STAT_E stat = WSS_IDLE;
    op_ret = wf_station_stat_get(&stat);
    if (OPRT_OK != op_ret) {
        PR_ERR("wf_station_stat_geterr:%d", op_ret);
        return op_ret;
    }

    if (WSS_GOT_IP != stat) {
        *nw_stat = STAT_STA_DISC;
    } else {
        *nw_stat = STAT_STA_CONN;
    }

    return OPRT_OK;
}

/**
* @brief upload_wifi_state
*
* @return  WM_SUCCESS 0/ WM_FAIL 1
*/
STATIC INT_T upload_wifi_state(VOID)
{
    UCHAR_T fr_type = WIFI_STATE_CMD;
    UCHAR_T data;
    GW_WIFI_NW_STAT_E wf_stat = 0;
    OPERATE_RET op_ret = OPRT_OK;
    op_ret = get_wf_gw_nw_status(&wf_stat);
    if (OPRT_OK != op_ret) {
        PR_ERR("get_wf_gw_nw_status error:%d", op_ret);
        return 0;
    }

    switch (wf_stat) {
    case STAT_UNPROVISION:
        data = WF_LIGHT_FAST_BLINK;
        break;

    case STAT_AP_STA_UNCFG:
        data = WF_LIGHT_SLOW_BLINK;
        break;

    case STAT_AP_STA_DISC:
    case STAT_STA_DISC:
        data = WF_LIGHT_OFF;
        break;

    case STAT_AP_STA_CONN:
    case STAT_STA_CONN:
        data = WF_LIGHT_ON;
        break;

    case STAT_CLOUD_CONN:
    case STAT_AP_CLOUD_CONN:
        if (ty_msg.prt_ver < 0x03) {
            data = WF_LIGHT_ON;
        } else {
            data = WF_MQ_ONLINE;
        }
        break;

    case STAT_LOW_POWER:
        if (ty_msg.prt_ver < 0x03) {
            data = WF_LIGHT_OFF;
        } else {
            data = WF_LOW_PWR;
        }
        break;
    case STAT_UNPROVISION_AP_STA_UNCFG:
        data = WF_EZ_AP_MODE;
        break;

    default:
        data = 0x00;
        break;
    }

    return ty_msg_send(fr_type, 1, &data);
}

/**
* @brief get_wifi_state
*
* @return  none
*/
STATIC VOID get_wifi_state(VOID)
{
    UCHAR_T fr_type = GET_WIFI_STATE_CMD;
    UCHAR_T data;
    GW_WIFI_NW_STAT_E wf_stat = 0;
    OPERATE_RET op_ret = OPRT_OK;

    if (WIFI_CONNECT_AP_TEST_NO != ty_msg.cont_ap_sta) {
        PR_NOTICE("ap connect test is ing wait test result sta");
        return;
    }

    op_ret = get_wf_gw_nw_status(&wf_stat);
    if (OPRT_OK != op_ret) {
        PR_ERR("get_wf_gw_nw_status error:%d", op_ret);
        return;
    }
    switch (wf_stat) {
    case STAT_UNPROVISION:
        data = WF_LIGHT_FAST_BLINK;
        break;

    case STAT_AP_STA_UNCFG:
        data = WF_LIGHT_SLOW_BLINK;
        break;

    case STAT_AP_STA_DISC:
    case STAT_STA_DISC:
        data = WF_LIGHT_OFF;
        break;

    case STAT_AP_STA_CONN:
    case STAT_STA_CONN:
        data = WF_LIGHT_ON;
        break;

    case STAT_CLOUD_CONN:
    case STAT_AP_CLOUD_CONN:
        data = WF_MQ_ONLINE;
        break;

    case STAT_LOW_POWER:
        data = WF_LOW_PWR;
        break;
    case STAT_UNPROVISION_AP_STA_UNCFG:
        data = WF_EZ_AP_MODE;
        break;

    default:
        data = 0x00;
        break;
    }

    ty_uart_send(fr_type, 1, &data);

    return;
}

/**
* @brief ty_bitmap_len_proc
*
* @return  len
*/
STATIC WORD_T ty_bitmap_len_proc(UINT_T bit_len)
{
    WORD_T len;

    len = bit_len / 8 + ((bit_len % 8 == 0) ? 0 : 1);
    if (len > 4)
        len = 4;
    return len;
}

/**
* @brief ty_bitmap_len_proc
*
* @return  dp
*/
STATIC UCHAR_T ty_dp_change_proc(UCHAR_T dp)
{
    dp++;
    return dp;
}

/**
* @brief ty_get_obj_type_len
*
* @return   WM_SUCCESS 0/ WM_FAIL 1
*/
STATIC INT_T ty_get_obj_type_len(TY_OBJ_DP_S *obj_dp, UCHAR_T *type, WORD_T *len)
{
    DEV_CNTL_N_S *dev_cntl = get_gw_dev_cntl();
    INT_T i = 0;
    DP_CNTL_S *dp_cntl = NULL;
    for (i = 0; i < dev_cntl->dp_num; i++) {
        if (dev_cntl->dp[i].dp_desc.dp_id == obj_dp->dpid) {
            dp_cntl = &dev_cntl->dp[i];
            break;
        }
    }

    if (NULL == dp_cntl) {
        PR_ERR("dp_cntl is NULL");
        return WM_FAIL;
    }

    if (dp_cntl->dp_desc.mode == M_RO) {
        PR_ERR("dp_cntl->dp_desc.mode is M_RO");
        return WM_FAIL;
    }

    if (dp_cntl->dp_desc.type == T_OBJ) {
        switch (dp_cntl->dp_desc.prop_tp) {
        case PROP_BOOL:
            *type = 0x01;
            *len = 0x01;
            break;
        case PROP_VALUE:
            *type = 0x02;
            *len = 0x04;
            break;
        case PROP_STR:
            *type = 0x03;
            *len = strlen(obj_dp->value.dp_str);
            break;
        case PROP_ENUM:
            *type = 0x04;
            *len = 0x01;
            break;
        case PROP_BITMAP:
            *type = 0x05;
            *len = ty_bitmap_len_proc(dp_cntl->prop.prop_bitmap.max_len);
            break;
        default:
            *type = 0x00;
            *len = 0;
            return WM_FAIL;
        }
    } else {
        PR_DEBUG("dp type is err (not obj_data)");
        return WM_FAIL;
    }
    return WM_SUCCESS;
}

/**
* @brief ty_data_proc
*
* @return Datalen
*/
STATIC INT_T ty_data_proc(TY_OBJ_DP_S *obj_dp, UCHAR_T type, WORD_T len, UCHAR_T *buf)
{
    INT_T base64_len = len;
    switch (type) {

    case DP_BOOL: {

        if (obj_dp->value.dp_bool == cJSON_False)
            buf[0] = 0x00;
        else
            buf[0] = 0x01;
    } break;

    case DP_VALUE: {
        UINT_T val_data = DWORD_SWAP(obj_dp->value.dp_value);
        memcpy(buf, &val_data, len);
    } break;

    case DP_STRING:
        if (obj_dp->value.dp_str) {
            memcpy(buf, obj_dp->value.dp_str, len);
        }
        break;
    case DP_ENUM: {
        buf[0] = (UCHAR_T)obj_dp->value.dp_enum;
    } break;
    case DP_BITMAP: {
        if (len == 1) {
            buf[0] = obj_dp->value.dp_bitmap;
        } else if (len == 2) {
            WORD_T bit = WORD_SWAP(obj_dp->value.dp_bitmap);
            memcpy(buf, &bit, len);
        } else {
            UINT_T bit = DWORD_SWAP(obj_dp->value.dp_bitmap);
            memcpy(buf, &bit, len);
        }
    } break;

    default:
        break;
    }
    return base64_len;
}

/**
* @brief __get_obj_dp_cnt
*
* @return   dp cnt
*/
WORD_T __get_obj_dp_cnt(WORD_T len, UCHAR_T *data)
{
    WORD_T obj_dp_cnt = 0;
    WORD_T offset = 0;
    WORD_T all_len = WORD_SWAP(len);
    TY_DATAPOINT_DATA_S *dp_data = NULL;

    while ((all_len - offset) >= TY_DP_FRAME_MIN) {
        dp_data = (TY_DATAPOINT_DATA_S *)(data + offset);
        offset += (4 + WORD_SWAP(dp_data->len));
        if (dp_data->type >= DP_BOOL && dp_data->type <= DP_BITMAP) {
            obj_dp_cnt++;
        }
    }
    return obj_dp_cnt;
}

/**
* @brief ty_raw_datapoint_proc
*
* @return none
*/
STATIC VOID ty_raw_datapoint_proc(TY_RECV_RAW_DP_S *raw_dp)
{
    if (NULL == raw_dp) {
        PR_ERR("ty_raw_datapoint_proc parm null");
        return;
    }

    WORD_T len = 0;
    len = raw_dp->len;

    UCHAR_T *SendBuf = (UCHAR_T *)Malloc(4 + len + 1);
    if (SendBuf == NULL) {
        PR_ERR("SendBuf is NULL");
        return;
    }
    memset(SendBuf, 0, 4 + len + 1);
    TY_DATAPOINT_DATA_S *dp_data = (TY_DATAPOINT_DATA_S *)SendBuf;
    dp_data->dpid = raw_dp->dpid;
    dp_data->type = 0x0;
    memcpy(dp_data->data, raw_dp->data, len);
    dp_data->len = WORD_SWAP(len);
    len = len + 4;
    ty_uart_send(DATA_CTRL_CMD, len, SendBuf);
    Free(SendBuf);
    SendBuf = NULL;
    return;
}

/**
* @brief ty_obj_datapoint_proc
*
* @return none
*/
STATIC VOID ty_obj_datapoint_proc(TY_OBJ_DP_S *obj_dp)
{
    UCHAR_T type = 0;
    WORD_T len = 0, rawlen = 0;
    INT_T ret = 0;

    ret = ty_get_obj_type_len(obj_dp, &type, &len);
    if (ret != WM_SUCCESS) {
        PR_ERR("ty_type_len_proc fail");
        return;
    }

    UCHAR_T *SendBuf = (UCHAR_T *)Malloc(4 + len + 1);
    if (SendBuf == NULL) {
        PR_ERR("SendBuf is NULL");
        return;
    }

    memset(SendBuf, 0, 4 + len + 1);
    TY_DATAPOINT_DATA_S *dp_data = (TY_DATAPOINT_DATA_S *)SendBuf;
    dp_data->dpid = obj_dp->dpid;
    dp_data->type = type;

    rawlen = ty_data_proc(obj_dp, type, len, dp_data->data);
    dp_data->len = WORD_SWAP(rawlen);
    rawlen = rawlen + 4;
    ty_uart_send(DATA_CTRL_CMD, rawlen, SendBuf);
    Free(SendBuf);
}

VOID_T __free_obj_dp_data(TY_OBJ_DP_S *dp_obj_data, WORD_T obj_dp_cnt)
{
    UINT_T i;
    TY_OBJ_DP_S *tmp;
    for (i = 0; i < obj_dp_cnt; i++) {
        tmp = dp_obj_data + i;
        if (tmp->type == PROP_STR) {
            if (tmp->value.dp_str) {
                Free(tmp->value.dp_str), tmp->value.dp_str = NULL;
            }
        }
    }
    if (dp_obj_data) {
        Free(dp_obj_data), dp_obj_data = NULL;
    }
}

/**
* @brief ty_datapoint_upload_proc
*
* @return none
*/
OPERATE_RET ty_datapoint_upload_proc(WORD_T len, UCHAR_T *data, WIFI_UP_TYPE up_type)
{
    WORD_T offset = 0;
    OPERATE_RET op_ret = OPRT_OK;
    WORD_T obj_dp_cnt = 0;
    TY_OBJ_DP_S *dp_obj_data = NULL;

    if (data == NULL) {
        PR_ERR("para err");
        return OPRT_INVALID_PARM;
    }

    if (!ty_msg.init_flag || ty_msg.IsInUpgrade) {
        PR_ERR("ty_mag not init or upgrade----ing init_flag = %d --- IsInUpgrade = %d", ty_msg.init_flag, ty_msg.IsInUpgrade);
        return OPRT_COM_ERROR;
    }
    GW_WIFI_NW_STAT_E cur_nw_stat;
    get_wf_gw_nw_status(&cur_nw_stat);
    if (((cur_nw_stat < STAT_STA_CONN) && (cur_nw_stat != STAT_AP_STA_CONN)) || (cur_nw_stat == STAT_UNPROVISION_AP_STA_UNCFG)) {
        PR_ERR("wifi status is invalid :%d", cur_nw_stat);
        return OPRT_NETWORK_ERROR;
    }
    DEV_CNTL_N_S *dev_cntl = get_gw_dev_cntl();
    if (dev_cntl == NULL) {
        PR_ERR("dev_cntl null");
        return OPRT_INVALID_PARM;
    }

    WORD_T all_len = WORD_SWAP(len);

#if DEBUG
    INT_T i = 0;
    PR_DEBUG("recv:");
    for (i = 0; i < all_len; i++) {
        PR_DEBUG_RAW("%02X ", data[i]);
    }
    PR_DEBUG("");
#endif

    obj_dp_cnt = __get_obj_dp_cnt(len, data);
    if (obj_dp_cnt > 0) {
        dp_obj_data = (TY_OBJ_DP_S *)Malloc(sizeof(TY_OBJ_DP_S) * obj_dp_cnt);
        if (dp_obj_data == NULL) {
            return OPRT_MALLOC_FAILED;
        }
        memset(dp_obj_data, 0, sizeof(TY_OBJ_DP_S) * obj_dp_cnt);
    }

    obj_dp_cnt = 0;

    TY_DATAPOINT_DATA_S *dp_data = NULL;
    while ((all_len - offset) >= TY_DP_FRAME_MIN) {
        dp_data = (TY_DATAPOINT_DATA_S *)(data + offset);
        if (dp_data->type > TY_DP_TYPE) {
            PR_ERR("dp_data->type=%d", dp_data->type);
            goto EXIT;
        }

        WORD_T dp_data_len = WORD_SWAP(dp_data->len);
        DP_CNTL_S *dp_cntl = get_dev_dp_cntl(NULL, dp_data->dpid);
        switch (dp_data->type) {
        case DP_RAW: {
            if ((dp_data_len >= all_len) || (dp_data_len > HN_RECV_BUF_MAX)) {
                PR_ERR("raw len is err:%d", dp_data_len);
                goto EXIT;
            }

            op_ret = dev_report_dp_raw_sync(get_gw_cntl()->gw_if.id, dp_data->dpid, dp_data->data, dp_data_len, 5);
            if (OPRT_OK != op_ret) {
                PR_ERR("dev_report_dp_raw_sync:%d", op_ret);
            }

        } break;

        case DP_BOOL: {
            if ((dp_data_len != 0x01) || (dp_data->data[0] < 0) || (dp_data->data[0] > 1)) {
                PR_ERR("dp_data->len:%d,dp_data->data[0]:%d", dp_data_len, dp_data->data[0]);
                goto EXIT;
            }
            if (dp_obj_data != NULL) {
                TY_OBJ_DP_S *obj_data_tmp = dp_obj_data + obj_dp_cnt;
                obj_data_tmp->dpid = dp_data->dpid;
                obj_data_tmp->type = PROP_BOOL;
                obj_data_tmp->value.dp_bool = dp_data->data[0];
                obj_dp_cnt++;
            }

        } break;

        case DP_VALUE: {
            if (dp_data_len != 0x04) {
                PR_ERR("dp_data->len = %d", dp_data_len);
                goto EXIT;
            }

            INT_T val_data = 0;
            memcpy(&val_data, dp_data->data, dp_data_len);
            val_data = DWORD_SWAP(val_data);
            if ((val_data < dp_cntl->prop.prop_value.min) || (val_data > dp_cntl->prop.prop_value.max)) {
                PR_ERR("val_data = %d", val_data);
                PR_ERR("data[0] = %d", dp_data->data[0]);
                PR_ERR("data[1] = %d", dp_data->data[1]);
                PR_ERR("data[2] = %d", dp_data->data[2]);
                PR_ERR("data[3] = %d", dp_data->data[3]);
                PR_ERR("min:%d,max%d", dp_cntl->prop.prop_value.min, dp_cntl->prop.prop_value.max);
                offset += (4 + dp_data_len);
                goto EXIT;
            }
            if (dp_obj_data != NULL) {
                TY_OBJ_DP_S *obj_data_tmp = dp_obj_data + obj_dp_cnt;
                obj_data_tmp->dpid = dp_data->dpid;
                obj_data_tmp->type = PROP_VALUE;
                obj_data_tmp->value.dp_value = val_data;
                obj_dp_cnt++;
            }
        } break;

        case DP_STRING: {
            if (dp_data_len > dp_cntl->prop.prop_str.max_len) {
                PR_ERR("dp_data->len=%d", dp_data_len);
                PR_ERR("dp_cntl->prop.prop_str.max_len=%d", dp_cntl->prop.prop_str.max_len);
                op_ret = OPRT_DP_TYPE_PROP_ILLEGAL;
                goto EXIT;
            }

            if (dp_obj_data != NULL) {
                TY_OBJ_DP_S *obj_data_tmp = dp_obj_data + obj_dp_cnt;
                obj_data_tmp->dpid = dp_data->dpid;
                obj_data_tmp->type = PROP_STR;
                obj_data_tmp->value.dp_str = (CHAR_T *)Malloc(dp_data_len + 1);
                if (obj_data_tmp->value.dp_str == NULL) {
                    op_ret = OPRT_MALLOC_FAILED;
                    goto EXIT;
                }
                memcpy(obj_data_tmp->value.dp_str, dp_data->data, dp_data_len);
                obj_data_tmp->value.dp_str[dp_data_len] = '\0';
                //obj_data_tmp->value.dp_str = dp_data->data;/*直接复用缓存内容*/
                obj_dp_cnt++;
            }
        } break;

        case DP_ENUM: {
            if (dp_data_len != 0x01) {
                PR_ERR("DP_ENUM dp_data->len=%d", dp_data_len);
                goto EXIT;
            }

            if (dp_obj_data != NULL) {
                TY_OBJ_DP_S *obj_data_tmp = dp_obj_data + obj_dp_cnt;
                obj_data_tmp->dpid = dp_data->dpid;
                obj_data_tmp->type = PROP_ENUM;
                obj_data_tmp->value.dp_enum = dp_data->data[0];
                obj_dp_cnt++;
            }
        } break;

        case DP_BITMAP: {
            if ((dp_data_len) > 4) {
                op_ret = OPRT_DP_TYPE_PROP_ILLEGAL;
                PR_ERR("dp_data->len:%d,dp_cntl->prop.prop_bitmap.max_len:%d", dp_data_len, dp_cntl->prop.prop_bitmap.max_len);
                goto EXIT;
            }
            INT_T bitdata = 0;
            if (dp_data_len == 1) {
                bitdata = dp_data->data[0];
            } else if (dp_data_len == 2) {
                bitdata = ((dp_data->data[0]) << 8) + dp_data->data[1];
            } else {
                bitdata = ((dp_data->data[0]) << 24) + ((dp_data->data[1]) << 16) + ((dp_data->data[2]) << 8) + dp_data->data[3];
            }

            if (dp_obj_data != NULL) {
                TY_OBJ_DP_S *obj_data_tmp = dp_obj_data + obj_dp_cnt;
                obj_data_tmp->dpid = dp_data->dpid;
                obj_data_tmp->type = PROP_BITMAP;
                obj_data_tmp->value.dp_bitmap = bitdata;
                obj_dp_cnt++;
            }
        } break;

        default:
            break;
        }
        offset += (4 + dp_data_len);
    }

    if (obj_dp_cnt != 0) { /*==0的时候只有raw，不上报*/
        op_ret = (WIFI_DATA_ASYNC_UP == up_type) ? dev_report_dp_json_async(get_gw_cntl()->gw_if.id, dp_obj_data, (UINT_T)obj_dp_cnt)
                                                 : dev_report_dp_stat_sync_extend(get_gw_cntl()->gw_if.id, dp_obj_data, (UINT_T)obj_dp_cnt, WIFI_SYNC_TIMEOUT, FALSE);
        if (OPRT_OK != op_ret) {
            PR_ERR("dp report err:%d %d", op_ret, up_type);
        }

        __free_obj_dp_data(dp_obj_data, obj_dp_cnt);
    }
    return op_ret;
EXIT:
    PR_ERR("op_ret:%d,raw dp or dp attribute is abnormal,dp_id=%d", op_ret, dp_data->dpid);
    __free_obj_dp_data(dp_obj_data, obj_dp_cnt);
    return OPRT_INVALID_PARM;
}

/**
* @brief jump_send_timer_cb
*
* @return none
*/
STATIC VOID jump_send_timer_cb(UINT_T timerID, PVOID_T pTimerArg)
{
    OPERATE_RET op_ret = OPRT_OK;

    if ((!ty_msg.IsInUpgrade) && (!ty_msg.StopHartBeat)) {
        send_jump_pack();
    }

    if (!ty_msg.init_flag || ty_msg.IsInUpgrade) {
        op_ret = sys_start_timer(ty_msg.jump_send_timer, JUMP_FIRST_TIME * 1000, TIMER_ONCE); //3s 心跳
        if (op_ret != OPRT_OK) {
            PR_ERR("jump_send_timer timer 3s start err");
        }
    } else {
        op_ret = sys_start_timer(ty_msg.jump_send_timer, JUMP_SEND_TIME * 1000, TIMER_ONCE); //15s 心跳
        if (op_ret != OPRT_OK) {
            PR_ERR("jump_send_timer timer 15s start err ");
        }
    }
}

/**
* @brief guard_timer_cb
*
* @return none
*/
STATIC VOID guard_timer_cb(UINT_T timerID, PVOID_T pTimerArg)
{
    if (!ty_msg.init_flag || ty_msg.IsInUpgrade || ty_msg.IsMcuUgReset || ty_msg.StopHartBeat) {
        return;
    }

    PR_NOTICE("guard time out,go to reset!!! ");

    tuya_hal_system_reset();
}

/**
* @brief start_guard_timer
*
* @return none
*/
STATIC VOID start_guard_timer(UCHAR_T sec)
{
    sys_start_timer(ty_msg.guard_timer, sec * 1000, TIMER_CYCLE);
}

/**
* @brief clear_all_queue
*
* @return none
*/
STATIC VOID clear_all_queue(VOID)
{
    INT_T i = 0, ret = 0;
    INT_T n = GetCurQueNum(ty_msg.pQueue);
    PR_DEBUG("queue num:%d", n);
    for (i = 0; i < n; i++) {
        TY_SEND_DATA_S *pSend = NULL;
        ret = OutQueue(ty_msg.pQueue, (UCHAR_T *)&pSend, 1);
        if (0 != ret) {
            PR_DEBUG("system size:%d", tuya_hal_system_getheapsize());
            Free(pSend);
            pSend = NULL;
        }
    }
}

/**
* @brief com_send_timer_cb
*
* @return none
*/
STATIC VOID com_send_timer_cb(UINT_T timerID, PVOID_T pTimerArg)
{
    PR_DEBUG("com time out");
    INT_T ret = 0;

    ty_msg.try_cnt++;
    if (ty_msg.try_cnt < 2) {
        return;
    }

    if (0 == GetCurQueNum(ty_msg.pQueue)) {
        return;
    }

    TY_SEND_DATA_S *pSend = NULL;
    ret = GetQueueMember(ty_msg.pQueue, 1, (UCHAR_T *)&pSend, 1);
    if ((0 == ret) || (NULL == pSend)) {
        return;
    }

    TY_FRAME_S *pFrame = (TY_FRAME_S *)pSend->data;
    if (pFrame != NULL) {
        if (pFrame->fr_type == HEART_BEAT_CMD) { //HB
            PR_NOTICE("heart timer out clear all");
            clear_all_queue();
        } else {
            PR_DEBUG("clear one");
            Free(pSend);
            DelQueueMember(ty_msg.pQueue, 1);
        }
    }
    ty_msg.try_cnt = 0;
}

/**
* @brief up_fw_timer_cb
*
* @return none
*/
STATIC VOID up_fw_timer_cb(UINT_T timerID, PVOID_T pTimerArg)
{

    PR_DEBUG("time out fw cb");
    ty_msg.ack_stat = FW_TIME_OUT;
    tuya_hal_semaphore_post(ty_msg.mcu_ug_sem);
}

/**
* @brief wifi_send_wifi_sta_test
*
* @return OPRT_OK/ERROR
*/
STATIC OPERATE_RET wifi_send_wifi_sta_test(IN CONST GW_WIFI_NW_STAT_E wf_sta)
{

    if (ty_msg.wf_work.mode == MCU_WIFI_MODE) {

        UCHAR_T fr_type = WIFI_STATE_CMD;
        UCHAR_T data = 0xff;

        switch (wf_sta) {
        case STAT_STA_DISC:
            data = WF_LIGHT_OFF;
            break;

        case STAT_STA_CONN:
            data = WF_LIGHT_ON;
            break;

        default:
            PR_NOTICE("wifi_send_wifi_sta_test wf_sta err:%d", wf_sta);
            break;
        }

        if (0xff == data) {
            return OPRT_COM_ERROR;
        }

        INT_T op_ret = ty_msg_send(fr_type, 1, &data);
        if (op_ret != WM_SUCCESS) {
            PR_ERR("ty_msg_send ERR");
            return OPRT_COM_ERROR;
        }

    } else if (ty_msg.wf_work.mode == WIFI_MODE) {

        switch (wf_sta) {

        case STAT_STA_DISC: {
            tuya_set_led_light_type(wifi_light, OL_HIGH, 0, 0);
            break;
        }

        case STAT_STA_CONN: {
            tuya_set_led_light_type(wifi_light, OL_LOW, 0, 0);
            break;
        }

        default: {
            PR_NOTICE("wifi_send_wifi_sta_test wf_sta err:%d", wf_sta);
            return OPRT_COM_ERROR;
        }
        }

    } else {
        PR_NOTICE("wifi_send_wifi_sta_test wf_work.mode err:%d", ty_msg.wf_work.mode);
        return OPRT_COM_ERROR;
    }

    return OPRT_OK;
}

/**
* @brief ap_connect_result_timer_cb
*
* @return OPRT_OK/ERROR
*/
STATIC VOID ap_connect_result_timer_cb(UINT_T timerID, PVOID_T pTimerArg)
{

    OPERATE_RET op_ret = OPRT_OK;

    STATIC BOOL_T wf_connect_bl = FALSE;
    STATIC GW_WIFI_NW_STAT_E wf_sta_last = 0xff;
    GW_WIFI_NW_STAT_E wf_sta = STAT_LOW_POWER;

    WF_WK_MD_E mode = WWM_LOWPOWER;
    op_ret = wf_wk_mode_get(&mode);
    if (OPRT_OK != op_ret) {
        PR_ERR("wf_wk_mode_get err:%d", op_ret);
        return;
    }

    if ((WIFI_CONNECT_AP_TEST_ING == ty_msg.cont_ap_sta) &&
        (mode != WWM_STATION)) {
        PR_NOTICE("test ing wf mode is not is station");
        wf_sta_last = 0xff;
        ty_msg.cont_ap_sta = WIFI_CONNECT_AP_TEST_OVER;
        sys_stop_timer(ty_msg.ap_test_timer);
        return;
    }

    op_ret = get_wf_connect_ap_test_sta(&wf_sta);
    if (OPRT_OK != op_ret) {
        PR_ERR("get_wf_connect_ap_test_sta err:%d", op_ret);
        return;
    }

    PR_DEBUG("ap_connect_result_timer_cb wf_sta :%d", wf_sta);

    if (wf_connect_bl == FALSE) {
        op_ret = wf_station_connect(ty_msg.ap_test_info.ap_ssid, ty_msg.ap_test_info.ap_passwd);
        if (op_ret != OPRT_OK) {
            PR_ERR("wf_station_connect err:%d", op_ret);
        } else {
            wf_connect_bl = TRUE;
        }
    }

    if (wf_sta != wf_sta_last) {
        op_ret = wifi_send_wifi_sta_test(wf_sta);
        if (OPRT_OK != op_ret) {
            PR_ERR("wifi_send_wifi_sta_test err:%d", op_ret);
            return;
        }
        wf_sta_last = wf_sta;
    }

    if (STAT_STA_CONN == wf_sta) {
        if (WIFI_CONNECT_AP_TEST_ING == ty_msg.cont_ap_sta) {
            wf_sta_last = 0xff;
            ty_msg.cont_ap_sta = WIFI_CONNECT_AP_TEST_OVER;
            sys_stop_timer(ty_msg.ap_test_timer);
        }
    }
}

/**
* @brief queue_send_timer_cb
*
* @return OPRT_OK/ERROR
*/
STATIC VOID queue_send_timer_cb(UINT_T timerID, PVOID_T pTimerArg)
{
    BOOL_T is_run = FALSE;
    TY_SEND_DATA_S *pSend = NULL;
    INT_T qNum = 0;

    is_run = IsThisSysTimerRun(ty_msg.com_send_timer);
    if (is_run) {
        return;
    }

    qNum = GetCurQueNum(ty_msg.pQueue);
    if (qNum > 0) {
        GetQueueMember(ty_msg.pQueue, 1, (UCHAR_T *)&pSend, 1);
        if (pSend == NULL) {
            PR_ERR("pSend is NULL");
            return;
        }

        tuya_hal_mutex_lock(ty_msg.mutex);
        ty_uart_send_data(TY_UART, pSend->data, pSend->len);
        tuya_hal_mutex_unlock(ty_msg.mutex);
        sys_start_timer(ty_msg.com_send_timer, QRY_SEND_TIMR_OUT_MS, TIMER_ONCE);

#ifdef UART_TX_DEBUG
        INT_T i = 0;
        for (i = 0; i < pSend->len; i++) {
            PR_DEBUG_RAW("%02X ", pSend->data[i]);
        }
#endif
    }
}

/**
* @brief recv_syn_datapoint_ack
*
* @return none
*/
STATIC VOID recv_syn_datapoint_ack(OPERATE_RET reason)
{
    UCHAR_T result = DATA_UP_OK;

    if (OPRT_OK == reason) {
        result = DATA_UP_OK;
    } else {
        result = DATA_UP_FAIL;
    }

    ty_uart_send(DATA_RPT_ACK_CMD, 1, &result);
}

/**
* @brief recv_func_get_wifi_mac
*
* @return none
*/
STATIC VOID recv_func_get_wifi_mac(VOID)
{

    OPERATE_RET op_ret = OPRT_OK;
    UCHAR_T mac_buf[7];
    memset(mac_buf, 0, SIZEOF(mac_buf));

    NW_MAC_S dev_mac;
    memset(&dev_mac, 0, SIZEOF(NW_MAC_S));
    op_ret = wf_get_mac(WF_STATION, &dev_mac);
    if (op_ret != OPRT_OK) {
        mac_buf[0] = GET_MAC_ERR;
        PR_ERR("wf_get_mac is err");
        goto EXIT;
    }

    mac_buf[0] = GET_MAC_OK;
    memcpy(&mac_buf[1], dev_mac.mac, SIZEOF(dev_mac.mac));

EXIT:
    ty_uart_send(GET_WIFI_MAC_CMD, SIZEOF(mac_buf), mac_buf);
    return;
}

/**
* @brief com_stop_timer_sons_cmd关闭发送超时检测
*
* @return none
*/
VOID com_stop_timer_sons_cmd(UCHAR_T cmd, UCHAR_T sons_cmd)
{
    del_queue_buf_by_sons_cmd(cmd, sons_cmd);
    sys_stop_timer(ty_msg.com_send_timer);
}

/**
* @brief get_timer_data
*
* @return OPRT_OK/ERROR
*/
STATIC OPERATE_RET get_timer_data(OUT POSIX_TM_S *p_st_time, CONST TIMER_SEVER_E type)
{
    OPERATE_RET op_ret = OPRT_OK;

    op_ret = uni_time_check_time_sync();
    if (op_ret != OPRT_OK) {
        PR_NOTICE("time wait go to sever get data");
        return OPRT_COM_ERROR;
    }

    if (type == UINX_TIMER) {
        op_ret = uni_time_get(p_st_time);
    } else {
        op_ret = uni_time_check_time_zone_sync();
        if (op_ret != OPRT_OK) {
            PR_NOTICE("time wait go to sever get zone");
            return OPRT_COM_ERROR;
        }
        op_ret = uni_local_time_get(p_st_time);
    }

    if (op_ret != OPRT_OK) {
        PR_NOTICE("get timer api err:%d", op_ret);
        return OPRT_COM_ERROR;
    }

    return OPRT_OK;
}









/**
* @brief ty_msg_upload_proc
*
* @return OPRT_OK/ERROR
*/
STATIC INT_T ty_msg_upload_proc(UCHAR_T *data)
{
    INT_T ret = 0;
    OPERATE_RET op_ret = OPRT_OK;
    TY_FRAME_S *ty_frame = (TY_FRAME_S *)data;
    STATIC BOOL_T s_net_flg = FALSE;

    switch (ty_frame->fr_type) {

    case HEART_BEAT_CMD: {
        recv_jump_pack(ty_frame);
    } break;

    case PRODUCT_INFO_CMD: {
        recv_prod_info(ty_frame);
    } break;

    case WORK_MODE_CMD: {
        ret = recv_work_mode(ty_frame);
        if (OPRT_OK == ret) {
            if ((MCU_WIFI_MODE == ty_msg.wf_work.mode) && (TRUE == s_net_flg)) {
                PR_NOTICE("work init again wifi net sta send");
                upload_wifi_state();
            }
            if (FALSE == s_net_flg) {
                s_net_flg = TRUE;
            }
        }
    } break;

    case WIFI_STATE_CMD: {
        recv_wifi_ack();
    } break;

    case WIFI_RESET_CMD: {
        reset_wifi_pro();
    } break;

    case WIFI_RESET_SEL_CMD: {
        reset_wifi_select(ty_frame);
    } break;

    case DATA_RPT_CMD:
        ty_datapoint_upload_proc(ty_frame->len, ty_frame->data, WIFI_DATA_ASYNC_UP);
        break;

    case DATA_RPT_SYN_CMD:
        op_ret = ty_datapoint_upload_proc(ty_frame->len, ty_frame->data, WIFI_DATA_SYNC_UP);
        recv_syn_datapoint_ack(op_ret);
        break;

    case DATA_QUERY_CMD:
        break;

    case UPDATE_START_CMD:
        recv_upgrade_start(ty_frame);
        break;

    case UPDATE_TRANS_CMD:
        recv_upgrade_transfer();
        break;

    case UT_TIME_CMD:
        recv_sys_datetime();
        break;

    case LOCAL_TIME_CMD:
        recv_local_datetime();
        break;

    case WIFI_TEST_CMD:
        recv_func_test();
        break;

    case MEMORY_QUERY_CMD:
        recv_mem_check();
        break;

    case GET_WF_SIGAL_CMD:
        send_wf_signal();
        break;

    case STOP_HEART_BEAT_CMD:
        recv_stop_heart_beat();
        break;

    case UART_CINFIG_NET_CMD:
        recv_uart_wf_net(ty_frame->fr_type, ty_frame->len, ty_frame->data);
        break;

    case GET_WIFI_STATE_CMD:
        get_wifi_state();
        break;

    case WIFI_CONNECT_AP_TEST_CMD:
        recv_func_ap_connect_test(ty_frame->len, ty_frame->data);
        break;

    case GET_WIFI_MAC_CMD:
        recv_func_get_wifi_mac();
        break;



    default:
        break;
    }

    return WM_SUCCESS;
}

/**
* @brief ty_uart_recv_proc
*
* @return none
*/
STATIC VOID ty_uart_recv_proc(VOID)
{

    UINT_T count = ty_uart_read_data(TY_UART, ty_msg.proc_buf + ty_msg.rcount, HN_RECV_BUF_MAX - ty_msg.rcount);
    ty_msg.rcount += count;
    if (ty_msg.rcount > 0) {
        ty_msg.recv_state = UART_PROC;
    }
}

/**
* @brief ty_dp_proc
*
* @return none
*/
STATIC VOID ty_dp_proc(VOID)
{
    WORD_T fr_len = 0;
    WORD_T offset = 0;
    UCHAR_T check_num = 0;
    while ((ty_msg.rcount - offset) >= HN_RECV_MIN_NUM) {
        if (ty_msg.proc_buf[offset] != 0x55) {
            offset += 1;
            continue;
        }
        if (ty_msg.proc_buf[offset + 1] != 0xaa) {
            offset += 1;
            continue;
        }

        fr_len = ty_msg.proc_buf[offset + 4] * 256 + ty_msg.proc_buf[offset + 5] + 6 + 1;
        if (fr_len > HN_RECV_BUF_MAX) {
            offset += 1;
            continue;
        }

        if (ty_msg.rcount - offset < fr_len) {
            ty_msg.timer_out_cnt++;
            if (TYU_RATE_115200 == uart_init_str.uart_bod) {
                tuya_hal_system_sleep(30);
            } else {
                tuya_hal_system_sleep(280);
            }
            if (ty_msg.timer_out_cnt > 4) {
                PR_NOTICE("pack is not over");
                ty_msg.timer_out_cnt = 0;
                offset += 2;
            }
            break;
        }
        ty_msg.timer_out_cnt = 0;

#ifdef UART_RX_DEBUG
        INT_T i = 0;
        PR_DEBUG("rx:\r\n");
        for (i = 0; i < fr_len; i++) {
            PR_DEBUG_RAW("%02X ", ty_msg.proc_buf[offset + i]);
        }
        PR_DEBUG("\r\n");
#endif
        check_num = getCheckSum(ty_msg.proc_buf + offset, fr_len - 1);
        if (check_num != ty_msg.proc_buf[offset + fr_len - 1]) {
            offset += 1;
            PR_NOTICE("crc offset is ....:%d", offset);
            continue;
        }
        ty_msg_upload_proc(ty_msg.proc_buf + offset);
        offset += fr_len;
    }

    if (offset > 0) {
        ty_msg.rcount -= offset;
        if (ty_msg.rcount > 0) {
            memmove(ty_msg.proc_buf, ty_msg.proc_buf + offset, ty_msg.rcount);
        }
    }
    ty_msg.recv_state = UART_RECV;
}

/**
* @brief ty_task_cb
*
* @return none
*/
STATIC VOID ty_task_cb(PVOID_T pArg)
{
    ty_msg.recv_state = UART_RECV;
    ty_msg.rcount = 0;

    while (1) {

        switch (ty_msg.recv_state) {
        case UART_RECV:
            ty_uart_recv_proc();
            break;
        case UART_PROC:
            ty_dp_proc();
            break;
        default:
            PR_DEBUG("########ty_task_cb default");
            break;
        }
    }
}

/**
* @brief set_wifi_light
*
* @return none
*/
VOID set_wifi_light(UCHAR_T stat)
{
    UCHAR_T fr_type = WIFI_STATE_CMD;
    UCHAR_T data = stat;

    ty_msg_send(fr_type, 1, &data);
}

/**
* @brief status_changed_cb
*
* @return none
*/
VOID status_changed_cb(IN CONST GW_STATUS_E status)
{
    PR_DEBUG("gw status changed to %d", status);
    if (GW_ACTIVED == status) {
        query_dp_state();
    }
}

/**
* @brief get_file_data_cb
*
* @return OPRT_OK/ERROR
*/
OPERATE_RET get_file_data_cb(IN CONST FW_UG_S *fw, IN CONST UINT_T total_len, IN CONST UINT_T offset,
                             IN CONST BYTE_T *data, IN CONST UINT_T len, OUT UINT_T *remain_len, IN PVOID_T pri_data)
{
    PR_DEBUG("Rev File Data");
    PR_DEBUG("Total_len:%d ", total_len);
    PR_DEBUG("Offset:%d Len:%d", offset, len);

    return OPRT_OK;
}

/**
* @brief upgrade_notify_cb
*
* @return none
*/
VOID upgrade_notify_cb(IN CONST FW_UG_S *fw, IN CONST INT_T download_result, IN PVOID_T pri_data)
{
    PR_DEBUG("download  Finish");
    PR_DEBUG("download_result:%d", download_result);
}

/**
* @brief stop_guard_timer
*
* @return none
*/
STATIC VOID stop_guard_timer(VOID)
{
    if (IsThisSysTimerRun(ty_msg.guard_timer) == TRUE) {
        sys_stop_timer(ty_msg.guard_timer);
    }
}

/**
* @brief gw_ug_inform_cb
*
* @return none
*/
VOID gw_ug_inform_cb(IN CONST FW_UG_S *fw)
{
    PR_DEBUG("Rev GW Upgrade Info");
    PR_DEBUG("fw->fw_url:%s", fw->fw_url);
    PR_DEBUG("fw->fw_hmac:%s", fw->fw_hmac);
    PR_DEBUG("fw->sw_ver:%s", fw->sw_ver);
    PR_DEBUG("fw->file_size:%d", fw->file_size);
    OPERATE_RET op_ret = OPRT_OK;

    memset(&dev_proc, 0, sizeof(DEV_UG_PROC_S));
    dev_proc.stat = MCU_GU_OK;
    ty_msg.IsInUpgrade = TRUE;
    dev_proc.file_size = fw->file_size;
    stop_guard_timer();

    op_ret = upgrade_pre_cb(dev_proc.file_size);
    if (op_ret != OPRT_OK) {
        PR_ERR("inform MCU to upgrade failed!");
        dev_proc.stat = MCU_GU_ERR;
        ty_msg.IsInUpgrade = FALSE;
        set_gw_ext_stat(EXT_NORMAL_S);
        return;
    }

    PR_DEBUG("start tuya_iot_upgrade_dev");
    op_ret = tuya_iot_upgrade_gw(fw, dev_ug_process_cb, dev_ug_notify_cb, NULL);
    if (OPRT_OK != op_ret) {
        PR_ERR("tuya_iot_upgrade_dev err:%d", op_ret);
        dev_proc.stat = MCU_GU_ERR;
        ty_msg.IsInUpgrade = FALSE;
        return;
    }
}

/**
* @brief dev_dp_query_cb
*
* @return none
*/
VOID dev_dp_query_cb(IN CONST TY_DP_QUERY_S *dp_qry)
{
    PR_DEBUG("Recv DP Query Cmd");
}

/**
* @brief dev_obj_dp_cb
*
* @return none
*/
VOID dev_obj_dp_cb(IN CONST TY_RECV_OBJ_DP_S *dp)
{
    if (!ty_msg.init_flag || ty_msg.IsInUpgrade || ty_msg.IsMcuUgReset) {
        PR_ERR("mcu is not ready initflag[%d] IsInUpgrade[%d] IsMcuReset[%d]", ty_msg.init_flag, ty_msg.IsInUpgrade, ty_msg.IsMcuUgReset);
        return;
    }

    INT_T i = 0;
    for (i = 0; i < dp->dps_cnt; i++) {
        ty_obj_datapoint_proc(&dp->dps[i]);
        tuya_hal_system_sleep(40);
    }
}

/**
* @brief dev_raw_dp_cb
*
* @return none
*/
VOID dev_raw_dp_cb(IN CONST TY_RECV_RAW_DP_S *dp)
{
    if (!ty_msg.init_flag || ty_msg.IsInUpgrade || ty_msg.IsMcuUgReset) {
        PR_ERR("mcu is not ready initflag[%d] IsInUpgrade[%d] IsMcuReset[%d]", ty_msg.init_flag, ty_msg.IsInUpgrade, ty_msg.IsMcuUgReset);
        return;
    }

    PR_DEBUG("dpid:%d recv len:%d", dp->dpid, dp->len);

    ty_raw_datapoint_proc(dp);
}

/**
* @brief query_all_data
*
* @return none
*/
STATIC VOID query_all_data(IN CONST GW_WIFI_NW_STAT_E stat)
{

    if (TRUE == uart_init_str.flow_open) {
        STATIC BOOL_T flag = FALSE;
        if (FALSE == flag) {
            flag = TRUE;
            query_dp_state();
        }
    } else {
        query_dp_state();
    }

    return;
}

/**
* @brief __get_wf_status
*
* @return none
*/
STATIC VOID __get_wf_status(IN CONST GW_WIFI_NW_STAT_E stat)
{

    PR_NOTICE("wifi status is :%d", stat);

    if (WIFI_CONNECT_AP_TEST_NO != ty_msg.cont_ap_sta) {
        PR_NOTICE("ap connect test is ing wait test result sta");
        return;
    }

    if (ty_msg.wf_work.mode == MCU_WIFI_MODE) {
        upload_wifi_state();
        if ((stat > STAT_STA_CONN) && (stat < STAT_UNPROVISION_AP_STA_UNCFG)) {
            query_all_data(stat);
        }
    } else if (ty_msg.wf_work.mode == WIFI_MODE) {

        if (stat > STAT_STA_CONN) {
            query_all_data(stat);
        }
        switch (stat) {
        case STAT_UNPROVISION: {
            tuya_set_led_light_type(wifi_light, OL_FLASH_HIGH, 250, LED_TIMER_UNINIT);
            break;
        }
        case STAT_AP_STA_UNCFG: {
            tuya_set_led_light_type(wifi_light, OL_FLASH_HIGH, 1500, LED_TIMER_UNINIT);
            break;
        }
        case STAT_LOW_POWER:
        case STAT_AP_STA_DISC:
        case STAT_STA_DISC: {
            tuya_set_led_light_type(wifi_light, OL_HIGH, 0, 0);
            break;
        }
        case STAT_AP_STA_CONN:
        case STAT_STA_CONN:
        case STAT_CLOUD_CONN:
        case STAT_AP_CLOUD_CONN: {
            tuya_set_led_light_type(wifi_light, OL_LOW, 0, 0);
            break;
        }
        case STAT_UNPROVISION_AP_STA_UNCFG: {
            tuya_set_led_light_type(wifi_light, OL_FLASH_HIGH, 250, LED_TIMER_UNINIT);
            break;
        }
        default: {
            tuya_set_led_light_type(wifi_light, OL_LOW, 0, 0);
            break;
        }
        }

    } else {
        PR_NOTICE("__get_wf_status work mode is err:%d", ty_msg.wf_work.mode);
    }
    return;
}

/**
* @brief uart_task_init
*
* @return none
*/
OPERATE_RET uart_task_init(IN TY_UART_BAUD_E uart_baud, IN CHAR_T *key_p, IN UINT_T key_len, IN BOOL_T flow_flg)
{
    OPERATE_RET op_ret = OPRT_OK;

    PR_DEBUG("device_init start");

    memset(&ty_msg, 0, SIZEOF(TY_MSG_S));
    memset(&uart_init_str, 0, SIZEOF(UART_INIT_S));

    ty_msg.online_flag = FALSE;
    ty_msg.init_flag = FALSE;
    ty_msg.prt_ver = 0x00;
    ty_msg.IsInUpgrade = FALSE;
    ty_msg.IsMcuUgReset = FALSE;
    ty_msg.StopHartBeat = FALSE;
    ty_msg.timer_out_cnt = 0;
    ty_msg.cont_ap_sta = WIFI_CONNECT_AP_TEST_NO;
    uart_init_str.uart_bod = uart_baud;
    uart_init_str.flow_open = flow_flg;
    mcu_info.cfg_mode = GWCM_OLD;
    mcu_info.start_mode = WF_START_SMART_FIRST;
    if (key_len > FIRMWARE_KEY_LEN_MAX) {
        memcpy(uart_init_str.firm_key, key_p, FIRMWARE_KEY_LEN_MAX);
    } else {
        memcpy(uart_init_str.firm_key, key_p, key_len);
    }
    PR_NOTICE("uart baud rate:%d----firmware key:%s", uart_baud, uart_init_str.firm_key);

    ty_uart_init(TY_UART, uart_baud, TYWL_8B, TYP_NONE, TYS_STOPBIT1, 1024, FALSE);

    ty_msg.pQueue = CreateQueueObj(QUEUE_MAX_EVENTS, SIZEOF(UCHAR_T *));
    if (ty_msg.pQueue == NULL) {
        PR_ERR("CreateQueueObj failed");
        return OPRT_COM_ERROR;
    }

    op_ret = sys_add_timer(jump_send_timer_cb, NULL, &ty_msg.jump_send_timer);
    if (OPRT_OK != op_ret) {
        PR_ERR("add jump_send_timer err");
        return op_ret;
    } else {
        sys_start_timer(ty_msg.jump_send_timer, 100, TIMER_ONCE); /*Speed up startup*/
    }

    op_ret = sys_add_timer(queue_send_timer_cb, NULL, &ty_msg.queue_send_timer);
    if (OPRT_OK != op_ret) {
        PR_ERR("add queue_send_timer err");
        return op_ret;
    } else {
        sys_start_timer(ty_msg.queue_send_timer, 50, TIMER_CYCLE);
    }

    op_ret = sys_add_timer(guard_timer_cb, NULL, &ty_msg.guard_timer);
    if (OPRT_OK != op_ret) {
        PR_ERR("add guard_timer_cb err");
        return op_ret;
    } else {
        start_guard_timer(WF_GUARD_TIME);
    }

    op_ret = sys_add_timer(com_send_timer_cb, NULL, &ty_msg.com_send_timer);
    if (OPRT_OK != op_ret) {
        PR_ERR("add com_send_timer err");
        return op_ret;
    }

    op_ret = sys_add_timer(up_fw_timer_cb, NULL, &ty_msg.up_fw_timer);
    if (OPRT_OK != op_ret) {
        PR_ERR("add up_fw_timer err");
        return op_ret;
    }

    op_ret = sys_add_timer(ap_connect_result_timer_cb, NULL, &ty_msg.ap_test_timer);
    if (OPRT_OK != op_ret) {
        PR_ERR("add queue_send_timer err");
        return op_ret;
    }

    
    op_ret = sys_timer_msg_funtion_init();
    if (op_ret != OPRT_OK) {
        PR_ERR("sys_timer_msg_funtion_init err");
        return op_ret;
    }

    op_ret = tuya_hal_mutex_create_init(&ty_msg.mutex);
    if (op_ret != OPRT_OK) {
        PR_ERR("create mutex err");
        return op_ret;
    }

    op_ret = tuya_hal_semaphore_create_init(&ty_msg.mcu_ug_sem, 0, 1);
    if (op_ret != OPRT_OK) {
        PR_ERR("create semphore err");
        return op_ret;
    }

    THRD_HANDLE ty_user_handle = NULL;
    THRD_PARAM_S thrd_param;
    thrd_param.priority = TRD_PRIO_1;
    thrd_param.stackDepth = 1024 * 3;
    thrd_param.thrdname = "ty_task";
    op_ret = CreateAndStart(&ty_user_handle, NULL, NULL,
                            ty_task_cb, NULL, &thrd_param);
    if (op_ret != OPRT_OK) {
        PR_ERR("start thread ty_task_cb err");
        return op_ret;
    }

    PR_NOTICE("uart_task_init ok");
    return OPRT_OK;
}

/**
* @brief key_process
*
* @return none
*/
STATIC VOID key_process(TY_GPIO_PORT_E port, PUSH_KEY_TYPE_E type, INT_T cnt)
{
    PR_DEBUG("port: %d", port);
    PR_DEBUG("type: %d", type);
    PR_DEBUG("cnt: %d", cnt);

    if (ty_msg.wf_work.reset_pin == port) {
        if (LONG_KEY == type) {
            tuya_iot_wf_gw_unactive(); /* AP  smart cfg   */
        }
    }
}

/**
* @brief dev_ug_inform_cb
*
* @return none
*/
VOID dev_ug_inform_cb(IN CONST CHAR_T *dev_id, IN CONST FW_UG_S *fw)
{
    PR_DEBUG("ERROR *************");
    PR_DEBUG("Rev GW Upgrade Info");
    PR_DEBUG("fw->fw_url:%s", fw->fw_url);
    PR_DEBUG("fw->fw_hmac:%s", fw->fw_hmac);
    PR_DEBUG("fw->sw_ver:%s", fw->sw_ver);
    PR_DEBUG("fw->file_size:%d", fw->file_size);
}

/**
* @brief dev_ug_process_cb
*
* @return OPRT_OK/ERROR
*/
STATIC OPERATE_RET dev_ug_process_cb(IN CONST FW_UG_S *fw, IN CONST UINT_T total_len, IN CONST UINT_T offset,
                                     IN CONST BYTE_T *data, IN CONST UINT_T len, OUT UINT_T *remain_len, IN PVOID_T pri_data)
{
    if (dev_proc.stat == MCU_GU_ERR) {
        return OPRT_COM_ERROR;
    }

    UINT_T send_len = 0;
    CONST UINT_T send_unit_len = get_unit_len();

    if ((len < send_unit_len) && (dev_proc.send_len + len < dev_proc.file_size)) {
        *remain_len = len;
        return OPRT_OK;
    }
    if (dev_proc.send_len >= dev_proc.file_size) {
        *remain_len = 0;
        return OPRT_OK;
    }
    PR_DEBUG("dev_proc.send_len = %d", dev_proc.send_len);
    OPERATE_RET op_ret = OPRT_OK;
    send_len = len;
    while (send_len >= send_unit_len) {
        op_ret = upgrade_file_cb(&data[len - send_len], send_unit_len);
        if (op_ret != OPRT_OK) {
            PR_ERR("MCU upgrade file failed!");
            return OPRT_COM_ERROR;
        }

        dev_proc.send_len += send_unit_len;
        send_len -= send_unit_len;
    }

    PR_DEBUG("send_len----%d---dev_proc.file_size :%d---dev_proc.send_len:%d", send_len, dev_proc.file_size, dev_proc.send_len);
    if ((send_len != 0) && (send_len >= dev_proc.file_size - dev_proc.send_len)) {
        op_ret = upgrade_file_cb(&data[len - send_len], dev_proc.file_size - dev_proc.send_len);
        if (op_ret != OPRT_OK) {
            PR_ERR("MCU upgrade file failed!");
            return OPRT_COM_ERROR;
        }
        send_len -= (dev_proc.file_size - dev_proc.send_len);
        dev_proc.send_len += (dev_proc.file_size - dev_proc.send_len);
    }

    if (dev_proc.send_len >= dev_proc.file_size) {
        PR_NOTICE("recv all data of user file");
    }
    *remain_len = send_len;
    return OPRT_OK;
}

/**
* @brief dev_ug_notify_cb
*
* @return none
*/
STATIC VOID dev_ug_notify_cb(IN CONST FW_UG_S *fw, IN CONST INT_T download_result, IN PVOID_T pri_data)
{
    if (OPRT_OK == download_result) { /* update success */
        return;
    } else {
        PR_ERR("the gateway upgrade failed");
        ty_msg.IsInUpgrade = FALSE;
    }
}



/**
 * @brief: reset proc callback
 * @param {GW_RESET_TYPE_E type -> reset reason} 
 * @retval: none
 */
STATIC VOID dev_ResetCB(GW_RESET_TYPE_E type)
{
    /* attention: before restart ,need to save in flash */
   
    return;
}

/**
 * @brief: reset proc callback
 * @param none
 * @retval: OPRT_OK/ERROR
 */
STATIC OPERATE_RET device_differ_init(VOID)
{

    OPERATE_RET op_ret = OPRT_OK;

    TY_IOT_CBS_S wf_cbs = {
        .gw_status_cb = status_changed_cb,
        .gw_ug_cb = gw_ug_inform_cb,
        .gw_reset_cb = dev_ResetCB,
        .dev_obj_dp_cb = dev_obj_dp_cb,
        .dev_raw_dp_cb = dev_raw_dp_cb,
        .dev_dp_query_cb = dev_dp_query_cb,
        .dev_ug_cb = dev_ug_inform_cb,
    };

    PR_NOTICE("cfg_mode:%d  start_mode :%d    firmware_key %s\r\n", mcu_info.cfg_mode, mcu_info.start_mode, uart_init_str.firm_key);

    UINT_T arr_idx = 0;
    GW_ATTACH_ATTR_T *arrt = (GW_ATTACH_ATTR_T *)Malloc(SIZEOF(GW_ATTACH_ATTR_T) * GW_ATTACH_ATTR_LMT);
    memset(arrt, 0, SIZEOF(GW_ATTACH_ATTR_T) * GW_ATTACH_ATTR_LMT);
    arrt[arr_idx].tp = mcu_info.tp;

    strncpy(arrt[arr_idx].ver, mcu_info.dev_sw_ver, SW_VER_LEN);
    PR_NOTICE("VER:%s,wf ver:%s,mcu tp :%d", arrt[arr_idx].ver, WF_SW_VERSION, mcu_info.tp);
    arr_idx++;

    op_ret = tuya_iot_wf_mcu_dev_init(mcu_info.cfg_mode, mcu_info.start_mode, &wf_cbs, uart_init_str.firm_key, mcu_info.product_key, WF_SW_VERSION, mcu_info.dev_sw_ver);
    if (OPRT_OK != op_ret) {
        PR_ERR("tuya_iot_wf_soc_dev_init err:%d", op_ret);
        Free(arrt);
        arrt = NULL;
        return -1;
    }
    Free(arrt);
    arrt = NULL;

    op_ret = tuya_iot_reg_get_wf_nw_stat_cb(__get_wf_status);
    if (OPRT_OK != op_ret) {
        PR_ERR("tuya_iot_reg_get_wf_nw_stat_cb err:%d", op_ret);
        return op_ret;
    }

    if (ty_msg.wf_work.mode == WIFI_MODE) {

        op_ret = tuya_create_led_handle(ty_msg.wf_work.state_pin, TRUE, &wifi_light);
        if (op_ret != OPRT_OK) {
            PR_ERR("tuya_create_led_handle err:%d", op_ret);
            return op_ret;
        }
        tuya_set_led_light_type(wifi_light, OL_HIGH, 0, 0);

        op_ret = key_init(NULL, 0, KEY_TIMER_MS);
        if (op_ret != OPRT_OK) {
            PR_ERR("key_init err:%d", op_ret);
            return op_ret;
        }

        KEY_USER_DEF_S rst_key = {ty_msg.wf_work.reset_pin, TRUE, LP_ONCE_TRIG, KEY_RST_TIME, 400, key_process};
        op_ret = reg_proc_key(&rst_key);
        if (op_ret != OPRT_OK) {
            PR_ERR("reg_proc_key err:%d", op_ret);
            return op_ret;
        }
    }

    return OPRT_OK;
}
